纹理分析详解 🎨
纹理分析就像是给图像做"指纹识别"!每种纹理都有其独特的"指纹",就像木纹的条纹、布料的编织、草地的随机分布一样。让我们一起来探索这个既有趣又实用的图像处理领域吧!
目录
- 1. 什么是纹理分析?
- 2. 灰度共生矩阵(GLCM)
- 3. 统计特征分析
- 4. 局部二值模式(LBP)
- 5. Gabor纹理特征
- 6. 纹理分类
- 7. 代码实现与优化
- 8. 实验结果与分析
1. 什么是纹理分析?
想象一下,你正在看一张木桌的照片。即使不看整体形状,你也能通过木纹的条纹认出这是木头。这就是纹理分析的魅力所在!它就像是在研究图像的"肌理",帮助我们理解图像的细节特征。
常见的纹理类型:
- 🌳 木纹:条状排列,就像树木的年轮
- 👕 布料:规则的编织方式,就像织毛衣的针法
- 🌱 草地:随机分布,就像撒在地上的芝麻
- 🧱 砖墙:规则排列,就像乐高积木
通过分析这些"指纹",我们可以:
- 🔍 识别不同材质(是木头还是石头?)
- ✂️ 进行图像分割(把木头和石头分开)
- 🎯 实现目标检测(找到所有的木头)
- 📊 评估表面质量(这块木头质量如何?)
2. 灰度共生矩阵(GLCM)
2.1 基本原理
GLCM就像是给图像做"像素配对"!它统计了图像中像素对的灰度关系,就像是在玩"找朋友"游戏。
举个例子:
- 如果两个像素的灰度值都是100,它们就是"好朋友"
- 如果一个是100,另一个是200,它们就是"普通朋友"
- GLCM就是统计这些"朋友关系"的频率
数学表达式:
P ( i , j ) = 像素对(i,j)的数量 总的像素对数量 P(i,j) = \frac{\text{像素对(i,j)的数量}}{\text{总的像素对数量}} P(i,j)=总的像素对数量像素对(i,j)的数量
2.2 Haralick特征
基于GLCM,我们可以提取多种有趣的纹理特征,就像是在给纹理做"体检":
-
对比度(Contrast):衡量像素对的差异程度
- 就像是在看"朋友之间的身高差"
- 差异越大,对比度越高
Contrast = ∑ i , j ∣ i − j ∣ 2 P ( i , j ) \text{Contrast} = \sum_{i,j} |i-j|^2 P(i,j) Contrast=i,j∑∣i−j∣2P(i,j)
-
相关性(Correlation):衡量像素对的线性关系
- 就像是在看"朋友之间的相似度"
- 相关性越高,说明纹理越规则
Correlation = ∑ i , j ( i − μ i ) ( j − μ j ) P ( i , j ) σ i σ j \text{Correlation} = \sum_{i,j} \frac{(i-\mu_i)(j-\mu_j)P(i,j)}{\sigma_i \sigma_j} Correlation=i,j∑σiσj(i−μi)(j−μj)P(i,j)
-
能量(Energy):衡量纹理的均匀程度
- 就像是在看"朋友关系的稳定性"
- 能量越高,说明纹理越均匀
Energy = ∑ i , j P ( i , j ) 2 \text{Energy} = \sum_{i,j} P(i,j)^2 Energy=i,j∑P(i,j)2
-
同质性(Homogeneity):衡量纹理的平滑程度
- 就像是在看"朋友之间的和谐度"
- 同质性越高,说明纹理越平滑
Homogeneity = ∑ i , j P ( i , j ) 1 + ( i − j ) 2 \text{Homogeneity} = \sum_{i,j} \frac{P(i,j)}{1+(i-j)^2} Homogeneity=i,j∑1+(i−j)2P(i,j)
2.3 代码实现
C++实现
Mat compute_glcm(const Mat& src, int distance, int angle) {Mat glcm = Mat::zeros(GRAY_LEVELS, GRAY_LEVELS, CV_32F);// Calculate offsetsint dx = 0, dy = 0;switch(angle) {case 0: dx = distance; dy = 0; break;case 45: dx = distance; dy = -distance; break;case 90: dx = 0; dy = -distance; break;case 135: dx = -distance; dy = -distance; break;default: dx = distance; dy = 0; break;}// Calculate GLCM#pragma omp parallel forfor(int i = 0; i < src.rows; i++) {for(int j = 0; j < src.cols; j++) {int ni = i + dy;int nj = j + dx;if(ni >= 0 && ni < src.rows && nj >= 0 && nj < src.cols) {int val1 = src.at<uchar>(i,j);int val2 = src.at<uchar>(ni,nj);#pragma omp atomicglcm.at<float>(val1,val2)++;}}}// Normalizeglcm /= sum(glcm)[0];return glcm;
}vector<double> extract_haralick_features(const Mat& glcm) {vector<double> features;features.reserve(4); // 4 Haralick featuresdouble contrast = 0, correlation = 0, energy = 0, homogeneity = 0;double mean_i = 0, mean_j = 0, std_i = 0, std_j = 0;// Calculate mean and standard deviationfor(int i = 0; i < GRAY_LEVELS; i++) {for(int j = 0; j < GRAY_LEVELS; j++) {double p_ij = static_cast<double>(glcm.at<float>(i,j));mean_i += i * p_ij;mean_j += j * p_ij;}}for(int i = 0; i < GRAY_LEVELS; i++) {for(int j = 0; j < GRAY_LEVELS; j++) {double p_ij = static_cast<double>(glcm.at<float>(i,j));std_i += (i - mean_i) * (i - mean_i) * p_ij;std_j += (j - mean_j) * (j - mean_j) * p_ij;}}std_i = sqrt(std_i);std_j = sqrt(std_j);// Calculate Haralick features#pragma omp parallel sections{#pragma omp section{for(int i = 0; i < GRAY_LEVELS; i++) {for(int j = 0; j < GRAY_LEVELS; j++) {double p_ij = static_cast<double>(glcm.at<float>(i,j));contrast += (i-j)*(i-j) * p_ij;}}}#pragma omp section{for(int i = 0; i < GRAY_LEVELS; i++) {for(int j = 0; j < GRAY_LEVELS; j++) {double p_ij = static_cast<double>(glcm.at<float>(i,j));correlation += ((i-mean_i)*(j-mean_j)*p_ij)/(std_i*std_j);}}}#pragma omp section{for(int i = 0; i < GRAY_LEVELS; i++) {for(int j = 0; j < GRAY_LEVELS; j++) {double p_ij = static_cast<double>(glcm.at<float>(i,j));energy += p_ij * p_ij;}}}#pragma omp section{for(int i = 0; i < GRAY_LEVELS; i++) {for(int j = 0; j < GRAY_LEVELS; j++) {double p_ij = static_cast<double>(glcm.at<float>(i,j));homogeneity += p_ij/(1+(i-j)*(i-j));}}}}features.push_back(contrast);features.push_back(correlation);features.push_back(energy);features.push_back(homogeneity);return features;
}
Python实现
def compute_glcm(img: np.ndarray, d: int = 1, theta: int = 0) -> np.ndarray:"""计算灰度共生矩阵(GLCM)Args:img: 输入图像d: 距离theta: 角度(0,45,90,135度)Returns:np.ndarray: GLCM矩阵"""# 确保图像是灰度图if len(img.shape) == 3:img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)# 量化灰度级levels = 8img = (img // (256 // levels)).astype(np.uint8)# 创建GLCM矩阵glcm = np.zeros((levels, levels), dtype=np.uint32)# 根据角度确定偏移if theta == 0:dx, dy = d, 0elif theta == 45:dx, dy = d, -delif theta == 90:dx, dy = 0, delse: # 135度dx, dy = -d, d# 计算GLCMh, w = img.shapefor i in range(h):for j in range(w):if 0 <= i+dy < h and 0 <= j+dx < w:glcm[img[i,j], img[i+dy,j+dx]] += 1# 归一化glcm = glcm.astype(np.float32)if np.sum(glcm) > 0:glcm /= np.sum(glcm)return glcmdef extract_haralick_features(glcm: np.ndarray) -> List[float]:"""提取Haralick特征Args:glcm: 灰度共生矩阵Returns:List[float]: Haralick特征(对比度、相关性、能量、同质性)"""# 计算均值和标准差rows, cols = glcm.shapemean_i = 0mean_j = 0# 计算均值for i in range(rows):for j in range(cols):mean_i += i * glcm[i, j]mean_j += j * glcm[i, j]# 计算标准差std_i = 0std_j = 0for i in range(rows):for j in range(cols):std_i += (i - mean_i)**2 * glcm[i, j]std_j += (j - mean_j)**2 * glcm[i, j]std_i = np.sqrt(std_i)std_j = np.sqrt(std_j)# 初始化特征contrast = 0correlation = 0energy = 0homogeneity = 0# 计算特征for i in range(rows):for j in range(cols):contrast += (i - j)**2 * glcm[i, j]if std_i > 0 and std_j > 0: # 防止除零correlation += ((i - mean_i) * (j - mean_j) * glcm[i, j]) / (std_i * std_j)energy += glcm[i, j]**2homogeneity += glcm[i, j] / (1 + (i - j)**2)return [contrast, correlation, energy, homogeneity]
3. 统计特征分析
3.1 一阶统计特征
这些特征就像是给纹理做"体检报告",告诉我们纹理的基本情况:
-
均值(Mean):纹理的平均灰度值
- 就像是在看"平均身高"
- 反映了纹理的整体亮度
μ = 1 N ∑ i = 1 N x i \mu = \frac{1}{N} \sum_{i=1}^N x_i μ=N1i=1∑Nxi
-
方差(Variance):纹理的灰度变化程度
- 就像是在看"身高差异"
- 反映了纹理的对比度
σ 2 = 1 N ∑ i = 1 N ( x i − μ ) 2 \sigma^2 = \frac{1}{N} \sum_{i=1}^N (x_i - \mu)^2 σ2=N1i=1∑N(xi−μ)2
-
偏度(Skewness):纹理的灰度分布偏斜程度
- 就像是在看"身高分布是否对称"
- 反映了纹理的不对称性
Skewness = 1 N σ 3 ∑ i = 1 N ( x i − μ ) 3 \text{Skewness} = \frac{1}{N\sigma^3} \sum_{i=1}^N (x_i - \mu)^3 Skewness=Nσ31i=1∑N(xi−μ)3
-
峰度(Kurtosis):纹理的灰度分布尖锐程度
- 就像是在看"身高分布是否集中"
- 反映了纹理的均匀性
Kurtosis = 1 N σ 4 ∑ i = 1 N ( x i − μ ) 4 − 3 \text{Kurtosis} = \frac{1}{N\sigma^4} \sum_{i=1}^N (x_i - \mu)^4 - 3 Kurtosis=Nσ41i=1∑N(xi−μ)4−3
3.2 代码实现
// 计算统计特征
vector<Mat> compute_statistical_features(const Mat& src, int window_size) {vector<Mat> features(4); // 均值、方差、偏度、峰度for(auto& feat : features) {feat.create(src.size(), CV_32F);}int half_size = window_size / 2;#pragma omp parallel for collapse(2)for(int i = 0; i < src.rows; i++) {for(int j = 0; j < src.cols; j++) {// 提取局部窗口Rect roi(max(0, j-half_size),max(0, i-half_size),min(window_size, src.cols-max(0,j-half_size)),min(window_size, src.rows-max(0,i-half_size)));Mat window = src(roi);// 计算统计特征double mean = compute_mean(window);double variance = compute_variance(window, mean);double std_dev = sqrt(variance);double skewness = compute_skewness(window, mean, std_dev);double kurtosis = compute_kurtosis(window, mean, std_dev);// 存储结果features[0].at<float>(i,j) = mean;features[1].at<float>(i,j) = variance;features[2].at<float>(i,j) = skewness;features[3].at<float>(i,j) = kurtosis;}}return features;
}// 计算均值
double compute_mean(const Mat& window) {Scalar mean = cv::mean(window);return mean[0];
}// 计算方差
double compute_variance(const Mat& window, double mean) {double variance = 0;#pragma omp parallel for reduction(+:variance)for (int i = 0; i < window.rows; i++) {for (int j = 0; j < window.cols; j++) {double diff = window.at<uchar>(i,j) - mean;variance += diff * diff;}}return variance / (window.rows * window.cols);
}// 计算偏度
double compute_skewness(const Mat& window, double mean, double std_dev) {double skewness = 0;#pragma omp parallel for reduction(+:skewness)for (int i = 0; i < window.rows; i++) {for (int j = 0; j < window.cols; j++) {double diff = (window.at<uchar>(i,j) - mean) / std_dev;skewness += diff * diff * diff;}}return skewness / (window.rows * window.cols);
}// 计算峰度
double compute_kurtosis(const Mat& window, double mean, double std_dev) {double kurtosis = 0;#pragma omp parallel for reduction(+:kurtosis)for (int i = 0; i < window.rows; i++) {for (int j = 0; j < window.cols; j++) {double diff = (window.at<uchar>(i,j) - mean) / std_dev;kurtosis += diff * diff * diff * diff;}}return kurtosis / (window.rows * window.cols) - 3.0;
}
4. 局部二值模式(LBP)
4.1 基本原理
LBP就像是给每个像素点做"二进制编码"!它通过比较中心像素与其邻域像素的大小关系,得到一个独特的"身份证号码"。
基本步骤:
- 选择一个中心像素(就像选一个"班长")
- 将其与邻域像素比较(就像"班长"和"同学们"比身高)
- 生成二进制编码(高个子记1,矮个子记0)
- 计算十进制值(把二进制转换成十进制)
示意图:
3 7 4 1 1 1 (128+64+32+
2 6 5 -> 0 1 -> 16+4) = 244
1 9 8 0 1 1
4.2 数学表达式
对于半径为R的圆形邻域中的P个采样点:
L B P P , R = ∑ p = 0 P − 1 s ( g p − g c ) 2 p LBP_{P,R} = \sum_{p=0}^{P-1} s(g_p - g_c)2^p LBPP,R=p=0∑P−1s(gp−gc)2p
其中:
- g c g_c gc 是中心像素的灰度值("班长"的身高)
- g p g_p gp 是邻域像素的灰度值("同学们"的身高)
- s ( x ) s(x) s(x) 是阶跃函数(判断谁高谁矮):
s ( x ) = { 1 , x ≥ 0 0 , x < 0 s(x) = \begin{cases} 1, & x \geq 0 \\ 0, & x < 0 \end{cases} s(x)={1,0,x≥0x<0
4.3 代码实现
C++实现
Mat compute_lbp(const Mat& src, int radius, int neighbors) {Mat dst = Mat::zeros(src.size(), CV_8U);vector<int> center_points_x(neighbors);vector<int> center_points_y(neighbors);// Pre-compute sampling point coordinatesfor(int i = 0; i < neighbors; i++) {double angle = 2.0 * CV_PI * i / neighbors;center_points_x[i] = static_cast<int>(radius * cos(angle));center_points_y[i] = static_cast<int>(-radius * sin(angle));}#pragma omp parallel forfor(int i = radius; i < src.rows-radius; i++) {for(int j = radius; j < src.cols-radius; j++) {uchar center = src.at<uchar>(i,j);uchar lbp_code = 0;for(int k = 0; k < neighbors; k++) {int x = j + center_points_x[k];int y = i + center_points_y[k];uchar neighbor = src.at<uchar>(y,x);lbp_code |= (neighbor > center) << k;}dst.at<uchar>(i,j) = lbp_code;}}return dst;
}
Python实现
def compute_lbp(img: np.ndarray, radius: int = 1,n_points: int = 8) -> np.ndarray:"""计算局部二值模式(LBP)Args:img: 输入图像radius: 半径n_points: 采样点数Returns:np.ndarray: LBP图像"""# 确保图像是灰度图if len(img.shape) == 3:img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)# 创建输出图像h, w = img.shapelbp = np.zeros((h, w), dtype=np.uint8)# 计算采样点坐标angles = np.linspace(0, 2*np.pi, n_points, endpoint=False)x = radius * np.cos(angles)y = radius * np.sin(angles)# 对每个像素计算LBPfor i in range(radius, h-radius):for j in range(radius, w-radius):center = img[i, j]pattern = 0for k in range(n_points):# 双线性插值获取采样点值x1 = int(j + x[k])y1 = int(i + y[k])x2 = x1 + 1y2 = y1 + 1# 计算插值权重wx = j + x[k] - x1wy = i + y[k] - y1# 双线性插值val = (1-wx)*(1-wy)*img[y1,x1] + \wx*(1-wy)*img[y1,x2] + \(1-wx)*wy*img[y2,x1] + \wx*wy*img[y2,x2]# 更新LBP模式pattern |= (val > center) << klbp[i, j] = patternreturn lbp
5. Gabor纹理特征
5.1 Gabor滤波器
Gabor滤波器就像是"纹理显微镜"!它可以在特定方向和尺度上观察纹理特征,就像是在用不同倍数的显微镜观察细胞。
二维Gabor滤波器的表达式:
g ( x , y ) = 1 2 π σ x σ y exp ( − x ′ 2 2 σ x 2 − y ′ 2 2 σ y 2 ) cos ( 2 π x ′ λ ) g(x,y) = \frac{1}{2\pi\sigma_x\sigma_y} \exp\left(-\frac{x'^2}{2\sigma_x^2}-\frac{y'^2}{2\sigma_y^2}\right)\cos(2\pi\frac{x'}{\lambda}) g(x,y)=2πσxσy1exp(−2σx2x′2−2σy2y′2)cos(2πλx′)
其中:
- x ′ = x cos θ + y sin θ x' = x\cos\theta + y\sin\theta x′=xcosθ+ysinθ(旋转后的x坐标)
- y ′ = − x sin θ + y cos θ y' = -x\sin\theta + y\cos\theta y′=−xsinθ+ycosθ(旋转后的y坐标)
- θ \theta θ 是方向角(显微镜的观察角度)
- λ \lambda λ 是波长(观察的精细程度)
- σ x \sigma_x σx 和 σ y \sigma_y σy 是高斯包络的标准差(观察的范围大小)
5.2 特征提取
- 生成Gabor滤波器组(准备不同倍数的"显微镜")
- 对图像进行滤波(用"显微镜"观察图像)
- 计算响应的统计特征(记录观察结果)
- 组合成特征向量(整理观察报告)
5.3 代码实现
C++实现
vector<Mat> generate_gabor_filters(int ksize, double sigma, int theta,double lambda, double gamma, double psi) {vector<Mat> filters;filters.reserve(theta);double sigma_x = sigma;double sigma_y = sigma/gamma;int half_size = ksize/2;// Generate Gabor filters for different orientationsfor(int t = 0; t < theta; t++) {double theta_rad = t * CV_PI / theta;Mat kernel(ksize, ksize, CV_32F);#pragma omp parallel forfor(int y = -half_size; y <= half_size; y++) {for(int x = -half_size; x <= half_size; x++) {// Rotationdouble x_theta = x*cos(theta_rad) + y*sin(theta_rad);double y_theta = -x*sin(theta_rad) + y*cos(theta_rad);// Gabor functiondouble gaussian = exp(-0.5 * (x_theta*x_theta/(sigma_x*sigma_x) +y_theta*y_theta/(sigma_y*sigma_y)));double harmonic = cos(2*CV_PI*x_theta/lambda + psi);kernel.at<float>(y+half_size,x+half_size) = static_cast<float>(gaussian * harmonic);}}// Normalizekernel = kernel / sum(abs(kernel))[0];filters.push_back(kernel);}return filters;
}vector<Mat> extract_gabor_features(const Mat& src,const vector<Mat>& filters) {vector<Mat> features;features.reserve(filters.size());Mat src_float;src.convertTo(src_float, CV_32F);// Apply convolution with each filter#pragma omp parallel forfor(int i = 0; i < static_cast<int>(filters.size()); i++) {Mat response;filter2D(src_float, response, CV_32F, filters[i]);// Calculate magnitudeMat magnitude;magnitude = abs(response);#pragma omp criticalfeatures.push_back(magnitude);}return features;
}
Python实现
def compute_gabor_features(img: np.ndarray,num_scales: int = 4,num_orientations: int = 6) -> np.ndarray:"""计算Gabor特征Args:img: 输入图像num_scales: 尺度数num_orientations: 方向数Returns:np.ndarray: Gabor特征图"""# 确保图像是灰度图if len(img.shape) == 3:img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)# 创建Gabor滤波器组filters = []for scale in range(num_scales):for orientation in range(num_orientations):# 计算Gabor参数theta = orientation * np.pi / num_orientationssigma = 2.0 * (2 ** scale)lambda_ = 4.0 * (2 ** scale)# 创建Gabor滤波器kernel = cv2.getGaborKernel((31, 31), sigma, theta, lambda_, 0.5, 0, ktype=cv2.CV_32F)filters.append(kernel)# 应用Gabor滤波器features = []for kernel in filters:filtered = cv2.filter2D(img, cv2.CV_32F, kernel)features.append(filtered)return np.array(features)
6. 纹理分类
6.1 基本原理
纹理分类就像是给不同的"布料"贴标签!我们需要:
- 提取特征(测量布料的"特征")
- 训练分类器(学习不同布料的"特点")
- 预测类别(给新布料"贴标签")
6.2 特征提取和选择
- GLCM特征(布料的"纹理规律")
- LBP特征(布料的"局部特征")
- Gabor特征(布料的"多尺度特征")
- 统计特征(布料的"整体特征")
6.3 分类算法
6.3.1 K近邻(K-NN)
K-NN就像是"物以类聚"!它通过找到K个最相似的样本,用它们的多数类别作为预测结果。
数学表达式:
y ^ = arg max c ∑ i = 1 K I ( y i = c ) \hat{y} = \arg\max_{c} \sum_{i=1}^K I(y_i = c) y^=argcmaxi=1∑KI(yi=c)
其中:
- y ^ \hat{y} y^ 是预测的类别
- y i y_i yi 是第i个近邻的类别
- I ( ⋅ ) I(\cdot) I(⋅) 是指示函数
- c c c 是类别标签
6.3.2 支持向量机(SVM)
SVM就像是"画一条线"!它试图找到一个最优的决策边界,使得不同类别的样本被最大间隔分开。
数学表达式:
min w , b 1 2 ∥ w ∥ 2 + C ∑ i = 1 n ξ i \min_{w,b} \frac{1}{2}\|w\|^2 + C\sum_{i=1}^n \xi_i w,bmin21∥w∥2+Ci=1∑nξi
约束条件:
y i ( w T x i + b ) ≥ 1 − ξ i , ξ i ≥ 0 y_i(w^T x_i + b) \geq 1 - \xi_i, \quad \xi_i \geq 0 yi(wTxi+b)≥1−ξi,ξi≥0
其中:
- w w w 是法向量
- b b b 是偏置项
- C C C 是惩罚参数
- ξ i \xi_i ξi 是松弛变量
6.4 代码实现
C++实现
// KNN分类器
class KNNClassifier {
private:std::vector<std::vector<float>> train_features;std::vector<int> train_labels;int k;public:KNNClassifier(int k = 5) : k(k) {}void train(const std::vector<std::vector<float>>& features,const std::vector<int>& labels) {train_features = features;train_labels = labels;}int predict(const std::vector<float>& feature) {std::vector<std::pair<float, int>> distances;#pragma omp parallel forfor(size_t i = 0; i < train_features.size(); i++) {float dist = 0;for(size_t j = 0; j < feature.size(); j++) {float diff = feature[j] - train_features[i][j];dist += diff * diff;}distances.push_back({std::sqrt(dist), train_labels[i]});}std::sort(distances.begin(), distances.end());std::vector<int> votes(k);for(int i = 0; i < k; i++) {votes[distances[i].second]++;}return std::max_element(votes.begin(), votes.end()) - votes.begin();}
};// SVM分类器
class SVMClassifier {
private:std::vector<std::vector<float>> support_vectors;std::vector<float> weights;float bias;float learning_rate;int max_iterations;public:SVMClassifier(float learning_rate = 0.001, int max_iterations = 1000): learning_rate(learning_rate), max_iterations(max_iterations) {}void train(const std::vector<std::vector<float>>& features,const std::vector<int>& labels) {int n_samples = features.size();int n_features = features[0].size();weights.resize(n_features, 0);bias = 0;for(int iter = 0; iter < max_iterations; iter++) {float error = 0;#pragma omp parallel for reduction(+:error)for(int i = 0; i < n_samples; i++) {float prediction = 0;for(int j = 0; j < n_features; j++) {prediction += weights[j] * features[i][j];}prediction += bias;float label = labels[i] * 2 - 1; // 转换为-1和1if(label * prediction < 1) {error += 1 - label * prediction;#pragma omp critical{for(int j = 0; j < n_features; j++) {weights[j] += learning_rate * (label * features[i][j] - 0.01 * weights[j]);}bias += learning_rate * label;}}}if(error == 0) break;}// 保存支持向量for(int i = 0; i < n_samples; i++) {float prediction = 0;for(int j = 0; j < n_features; j++) {prediction += weights[j] * features[i][j];}prediction += bias;if(std::abs(prediction) < 1) {support_vectors.push_back(features[i]);}}}int predict(const std::vector<float>& feature) {float prediction = 0;for(size_t i = 0; i < feature.size(); i++) {prediction += weights[i] * feature[i];}prediction += bias;return prediction > 0 ? 1 : 0;}
};
Python实现
class KNNClassifier:"""K近邻分类器"""def __init__(self, k=5):self.k = kself.train_features = Noneself.train_labels = Nonedef train(self, features, labels):"""训练模型参数:features: 训练特征labels: 训练标签"""self.train_features = np.array(features)self.train_labels = np.array(labels)def predict(self, feature):"""预测单个样本的类别参数:feature: 输入特征返回:predicted_label: 预测的类别"""# 计算距离distances = np.sqrt(np.sum((self.train_features - feature) ** 2, axis=1))# 获取k个最近邻的索引k_indices = np.argsort(distances)[:self.k]# 获取k个最近邻的标签k_nearest_labels = self.train_labels[k_indices]# 返回出现次数最多的标签return np.bincount(k_nearest_labels).argmax()class SVMClassifier:"""支持向量机分类器"""def __init__(self, learning_rate=0.001, max_iterations=1000):self.learning_rate = learning_rateself.max_iterations = max_iterationsself.weights = Noneself.bias = Noneself.support_vectors = Nonedef train(self, features, labels):"""训练模型参数:features: 训练特征labels: 训练标签"""n_samples, n_features = np.array(features).shape# 初始化参数self.weights = np.zeros(n_features)self.bias = 0# 将标签转换为-1和1y = np.array(labels) * 2 - 1for _ in range(self.max_iterations):error = 0for i in range(n_samples):prediction = np.dot(self.weights, features[i]) + self.biasif y[i] * prediction < 1:error += 1 - y[i] * prediction# 更新权重和偏置self.weights += self.learning_rate * (y[i] * features[i] - 0.01 * self.weights)self.bias += self.learning_rate * y[i]if error == 0:break# 保存支持向量self.support_vectors = []for i in range(n_samples):prediction = np.dot(self.weights, features[i]) + self.biasif abs(prediction) < 1:self.support_vectors.append(features[i])def predict(self, feature):"""预测单个样本的类别参数:feature: 输入特征返回:predicted_label: 预测的类别"""prediction = np.dot(self.weights, feature) + self.biasreturn 1 if prediction > 0 else 0
7. 性能优化技巧
7.1 并行计算
- 使用OpenMP进行并行计算(就像"多线程跑步")
- 合理设置线程数(不要"人太多挤在一起")
- 避免线程竞争(不要"抢跑道")
7.2 内存优化
- 使用连续内存(就像"排好队")
- 避免频繁的内存分配(不要"总是搬家")
- 使用内存池(就像"提前准备好房间")
7.3 算法优化
- 使用查找表(就像"提前背好答案")
- 减少重复计算(不要"重复做同一件事")
- 使用SIMD指令(就像"一次做多件事")
8. 总结
纹理分析就像是在给图像做"指纹识别",每种纹理都有其独特的"指纹"!通过GLCM、LBP和Gabor等方法,我们可以有效地提取和分析这些"指纹"。在实际应用中,需要根据具体场景选择合适的方法,就像选择不同的"显微镜"来观察不同的样本。
记住:好的纹理分析就像是一个经验丰富的"纹理侦探",能够从图像的细节中发现重要的线索!🔍
9. 参考资料
- Haralick R M. Statistical and structural approaches to texture[J]. Proceedings of the IEEE, 1979
- Ojala T, et al. Multiresolution gray-scale and rotation invariant texture classification with local binary patterns[J]. IEEE TPAMI, 2002
- OpenCV官方文档: https://docs.opencv.org/
- 更多资源: IP101项目主页