【IP101】紋理特征提取與分析:從統計方法到深度表征的系統解析

紋理分析詳解 🎨

紋理分析就像是給圖像做"指紋識別"!每種紋理都有其獨特的"指紋",就像木紋的條紋、布料的編織、草地的隨機分布一樣。讓我們一起來探索這個既有趣又實用的圖像處理領域吧!

目錄

  • 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,我們可以提取多種有趣的紋理特征,就像是在給紋理做"體檢":

  1. 對比度(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?j2P(i,j)
  2. 相關性(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)?
  3. 能量(Energy):衡量紋理的均勻程度

    • 就像是在看"朋友關系的穩定性"
    • 能量越高,說明紋理越均勻
      Energy = ∑ i , j P ( i , j ) 2 \text{Energy} = \sum_{i,j} P(i,j)^2 Energy=i,j?P(i,j)2
  4. 同質性(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 一階統計特征

這些特征就像是給紋理做"體檢報告",告訴我們紋理的基本情況:

  1. 均值(Mean):紋理的平均灰度值

    • 就像是在看"平均身高"
    • 反映了紋理的整體亮度
      μ = 1 N ∑ i = 1 N x i \mu = \frac{1}{N} \sum_{i=1}^N x_i μ=N1?i=1N?xi?
  2. 方差(Variance):紋理的灰度變化程度

    • 就像是在看"身高差異"
    • 反映了紋理的對比度
      σ 2 = 1 N ∑ i = 1 N ( x i ? μ ) 2 \sigma^2 = \frac{1}{N} \sum_{i=1}^N (x_i - \mu)^2 σ2=N1?i=1N?(xi??μ)2
  3. 偏度(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σ31?i=1N?(xi??μ)3
  4. 峰度(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σ41?i=1N?(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. 選擇一個中心像素(就像選一個"班長")
  2. 將其與鄰域像素比較(就像"班長"和"同學們"比身高)
  3. 生成二進制編碼(高個子記1,矮個子記0)
  4. 計算十進制值(把二進制轉換成十進制)

示意圖:

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=0P?1?s(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,?x0x<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?σy?1?exp(?2σx2?x′2??2σy2?y′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 特征提取

  1. 生成Gabor濾波器組(準備不同倍數的"顯微鏡")
  2. 對圖像進行濾波(用"顯微鏡"觀察圖像)
  3. 計算響應的統計特征(記錄觀察結果)
  4. 組合成特征向量(整理觀察報告)

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 基本原理

紋理分類就像是給不同的"布料"貼標簽!我們需要:

  1. 提取特征(測量布料的"特征")
  2. 訓練分類器(學習不同布料的"特點")
  3. 預測類別(給新布料"貼標簽")

6.2 特征提取和選擇

  1. GLCM特征(布料的"紋理規律")
  2. LBP特征(布料的"局部特征")
  3. Gabor特征(布料的"多尺度特征")
  4. 統計特征(布料的"整體特征")

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^?=argcmax?i=1K?I(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,bmin?21?w2+Ci=1n?ξ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 并行計算

  1. 使用OpenMP進行并行計算(就像"多線程跑步")
  2. 合理設置線程數(不要"人太多擠在一起")
  3. 避免線程競爭(不要"搶跑道")

7.2 內存優化

  1. 使用連續內存(就像"排好隊")
  2. 避免頻繁的內存分配(不要"總是搬家")
  3. 使用內存池(就像"提前準備好房間")

7.3 算法優化

  1. 使用查找表(就像"提前背好答案")
  2. 減少重復計算(不要"重復做同一件事")
  3. 使用SIMD指令(就像"一次做多件事")

8. 總結

紋理分析就像是在給圖像做"指紋識別",每種紋理都有其獨特的"指紋"!通過GLCM、LBP和Gabor等方法,我們可以有效地提取和分析這些"指紋"。在實際應用中,需要根據具體場景選擇合適的方法,就像選擇不同的"顯微鏡"來觀察不同的樣本。

記住:好的紋理分析就像是一個經驗豐富的"紋理偵探",能夠從圖像的細節中發現重要的線索!🔍

9. 參考資料

  1. Haralick R M. Statistical and structural approaches to texture[J]. Proceedings of the IEEE, 1979
  2. Ojala T, et al. Multiresolution gray-scale and rotation invariant texture classification with local binary patterns[J]. IEEE TPAMI, 2002
  3. OpenCV官方文檔: https://docs.opencv.org/
  4. 更多資源: IP101項目主頁

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/81593.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/81593.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/81593.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

機器學習中采樣哪些事

在機器學習中采樣主要分為兩種&#xff0c;過采樣&#xff08;Oversample&#xff09;和欠采樣(Undersample)。過采樣就是通過增加少數類樣本的數量來平衡數據集。而欠采樣就是通過減少多數類樣本的數量來平衡數據集。 通常在進行采樣中以下是幾種常用的方法: 1. 隨機采樣 隨…

fastadmin 數據導出,設置excel行高和限制圖片大小

fastadmin默認導出圖片全部都再一塊&#xff0c;而且不在單元格里 話不多說&#xff0c;上代碼 修改文件的路徑&#xff1a; /public/assets/js/require-table.js exportOptions: {fileName: export_ Moment().format("YYYY-MM-DD"),preventInjection: false,mso…

鴻蒙OSUniApp開發的商品詳情展示頁面(鴻蒙系統適配版)#三方框架 #Uniapp

使用UniApp開發的商品詳情展示頁面&#xff08;鴻蒙系統適配版&#xff09; 前言 隨著移動電商的普及&#xff0c;一個體驗良好的商品詳情頁對于提高用戶轉化率至關重要。本文將分享我在使用UniApp開發商品詳情頁時的實踐經驗&#xff0c;并特別關注如何適配鴻蒙系統&#xf…

redis中key的過期和淘汰

一、過期&#xff08;redis主動刪除&#xff09; 設置了ttl過期時間的key&#xff0c;在ttl時間到的時候redis會刪除過期的key。但是redis是惰性過期。惰性過期&#xff1a;redis并不會立即刪除過期的key&#xff0c;而是會在獲取key的時候判斷key是否過期&#xff0c;如果發現…

Qwen3 - 0.6B與Bert文本分類實驗:深度見解與性能剖析

Changelog [25/04/28] 新增Qwen3-0.6B在Ag_news數據集Zero-Shot的效果。新增Qwen3-0.6B線性層分類方法的效果。調整Bert訓練參數&#xff08;epoch、eval_steps&#xff09;&#xff0c;以實現更細致的觀察&#xff0c;避免嚴重過擬合的情況。 TODO&#xff1a; 利用Qwen3-0.6…

HTML應用指南:利用POST請求獲取全國京東快遞服務網點位置信息

京東快遞作為中國領先的智能供應鏈與綜合物流服務提供商,自2007年成立以來,始終致力于通過技術創新與高效運營,為客戶提供安全、可靠、快速的物流解決方案。京東快遞依托京東集團的強大資源支持,憑借其自營倉儲、干線運輸、末端配送一體化的物流網絡,在激烈的市場競爭中脫…

js中eval的用法風險與替代方案全面解析

1. 前言 在 JavaScript 里&#xff0c;eval是一個既強大又充滿爭議的函數。它為開發者提供了一種動態執行字符串代碼的能力&#xff0c;在某些特定場景下能發揮出獨特的作用。然而&#xff0c;由于其特殊的運行機制&#xff0c;也帶來了諸多潛在的風險和問題。本文將深入探討e…

antd樹結構

一、場景實現 1、左側為查詢條件&#xff0c;查詢條件為樹和多選。點擊查詢條件在右上方顯示搜索條件的內容&#xff0c;右上方查詢條件 tag 刪除后&#xff0c;左側條件也對應刪除。 2、樹結構&#xff1a;默認第一層下所有節點都展開。 1、頁面效果圖 2、查詢效果圖 二、前端…

Jenkins 安裝與配置指南

Jenkins 安裝與配置指南&#xff08;MD 示例&#xff09; markdown Jenkins 安裝與配置指南 ## 一、環境準備 1. **系統要求** - 操作系統&#xff1a;Linux/macOS/Windows - Java 版本&#xff1a;JDK 8 或更高&#xff08;建議 JDK 11&#xff09;2. **安裝方式** - **L…

[Linux性能優化] 線程卡頓優化。Linux加入USB(HID)熱插拔線程占用CPU優化。Linux中CPU使用率過高優化

文章目錄 [Linux性能優化] 線程卡頓優化。0、省流版本一、問題定位&#xff1a;CPU 資源分析二、線程卡頓現場復現線程優化前圖片 三、線程卡頓優化方向1.如果是輪詢方式2.如果是事件驅動方式 四、修改方式線程優化后圖片 [Linux性能優化] 線程卡頓優化。 0、省流版本 如果采…

ip與mac-數據包傳輸過程學習

你管這破玩意叫網絡&#xff1f; 內容來源于飛天閃客&#xff0c;以前沒有學習過網絡的相關基礎知識&#xff0c;只會去瞎設置&#xff0c;現在終于是弄明白了。 多臺電腦之間想要通信&#xff0c;可以直接通過一條網線進行連接。但是隨著網線的增加&#xff0c;這個就會比較…

數值分析知識重構

數值分析知識重構 一 Question 請構造一下數值分析中的誤差概念以及每一個具體數值方法的誤差是如何估計的&#xff1f; 二 Question 已知 n 1 n1 n1個數據點 ( x i , y i ) , i 0 , 1 , ? , n (x_i,y_i),i0,1,\cdots,n (xi?,yi?),i0,1,?,n,請使用多種方法建立數據之間…

使用 Qt QGraphicsView/QGraphicsScene 繪制色輪

使用 Qt QGraphicsView/QGraphicsScene 繪制色輪 本文介紹如何在 Qt 中利用 QGraphicsView 和 QGraphicsScene 實現基礎圓形繪制&#xff0c;以及進階的色輪&#xff08;Color Wheel&#xff09;效果。 色輪是色彩選擇器的常見控件&#xff0c;廣泛應用于圖形設計、繪畫和 UI …

移除鏈表元素數據結構oj題(力扣題206)

目錄 題目描述&#xff1a; 題目解讀&#xff08;分析&#xff09; 解決代碼 題目描述&#xff1a; 給你一個鏈表的頭節點 head 和一個整數 val &#xff0c;請你刪除鏈表中所有滿足 Node.val val 的節點&#xff0c;并返回 新的頭節點 。 題目解讀&#xff08;分析&#…

GLPK(GNU線性規劃工具包)中建模語言MathProg的使用

GNU MathProg是一種用于描述線性數學規劃模型的建模語言。用GNU MathProg語言編寫的模型描述由一組語句和數據塊組成。 在MathProg中&#xff0c;模型以集合、參數、變量、約束和目標(sets, parameters, variables, constraints, objectives稱為模型對象)的形式進行描述。 在Ma…

《Python星球日記》 第77天:模型部署與總結

名人說:路漫漫其修遠兮,吾將上下而求索。—— 屈原《離騷》 創作者:Code_流蘇(CSDN)(一個喜歡古詩詞和編程的Coder??) 目錄 一、模型部署技術1. 模型文件導出不同模型格式對比2. 使用Flask構建RESTful API3. 使用FastAPI構建高性能API4. 部署優化與最佳實踐二、部署架構…

【JavaWeb】MySQL(準備篇)

1 MySQL安裝 1.1 解壓 下載完成后我們得到的是一個壓縮包&#xff08;所有文件均在文末安裝包中&#xff09;&#xff0c;將其解壓&#xff0c;我們就可以得到MySQL 8.0.34 的軟件本體了(就是一個文件夾)&#xff0c;我們可以把它放在你想安裝的位置 。 1.2 配置 1.2.1 配置…

國產數據庫工具突圍:SQLynx如何解決Navicat的三大痛點?深度體驗報告

引言&#xff1a;Navicat的"中國困境" 當開發者面對達夢數據庫的存儲過程調試&#xff0c;或是在人大金倉中處理復雜查詢時&#xff0c;Navicat突然變得力不從心——這不是個例。 真實痛點&#xff1a;某政務系統遷移至OceanBase后&#xff0c;開發團隊發現Navicat無…

ETL數據集成產品選型需要關注哪些方面?

ETL&#xff08;Extract&#xff0c;Transform&#xff0c;Load&#xff09;工具作為數據倉庫和數據分析流程中的關鍵環節&#xff0c;其選型對于企業的數據戰略實施有著深遠的影響。谷云科技在 ETL 領域耕耘多年&#xff0c;通過自身產品的實踐應用&#xff0c;對 ETL 產品選型…

數據結構實驗10.1:內部排序的基本運算

文章目錄 一&#xff0c;實驗目的二&#xff0c;實驗內容1. 數據生成與初始化2. 排序算法實現&#xff08;1&#xff09;直接插入排序&#xff08;2&#xff09;二分插入排序&#xff08;3&#xff09;希爾排序&#xff08;4&#xff09;冒泡排序&#xff08;5&#xff09;快速…