使用ORB提取图像特征维度不一致无法训练传统ML分类器问题
问题描述
我是机器学习初学者,目前正在开展二分类图像分类项目,任务是完成COVID、正常两类医学影像的预测分类。使用ORB特征检测器与描述子提取图像特征时遇到问题:不同图像输出的特征向量长度不一致,无法直接输入SVM、KNN等传统机器学习算法训练。(为简化展示,下方代码暂未包含正常类图像的处理逻辑)
原有实现代码
%matplotlib inline from sklearn.model_selection import train_test_split from matplotlib import pyplot as plt from sklearn import tree import numpy as np import random import cv2 import os print (cv2.__version__) path1 = 'D:\\Semester 6\\ML\\Image Classification\\Testing\\Train\\COVID' path2 = 'D:\\Semester 6\\ML\\Image Classification\\Testing\\Train\\Normal' images1 = [] images2 = [] myListC = os.listdir(path1) myListN = os.listdir(path2) orb = cv2.ORB_create() for clC in myListC: imgCur1 = cv2.imread(f'{path1}/{clC}', 0) imgCur1 = cv2.resize(imgCur1, (200,200)) print(len(imgCur1)) images1.append(imgCur1) desListC = [] for img in images1: kp1, des1 = orb.detectAndCompute(img, None) e, z = des1.shape des1 = des1.reshape(e*z) print(des1.shape) desListC.append(des1) desList = np.array(desList) X = [] y = [] for i in range(len(desList)): X.append(desList[i][:-1]) for i in range(len(desList)): y.append(desList[i][-1:]) X = np.array(X) y = np.array(y) X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.33, random_state=42) clf = tree.DecisionTreeClassifier() clf = clf.fit(X, y)
运行异常现象
- 不同图像提取的ORB描述子展平后维度不统一,输出形状如下:

- 代码运行报错信息如下:

- 核心问题:代码直接将每张图的ORB描述子展平后尝试构造数据集,由于不同图像检测到的关键点数量不同,展平后特征维度不统一,无法构造规整numpy数组,也无法完成分类器拟合。
解决方案
ORB算法本身不会为每张图返回固定数量的关键点,每个ORB描述子固定为32维二进制向量,直接展平必然出现维度不齐的问题,同时原有代码的标签构造逻辑存在错误(不应该从描述子数组中切分标签,需要手动根据图像类别赋值),可按以下方法修复:
- 快速修复方案:初始化ORB检测器时固定最大关键点检测数量,对提取到的描述子做截断/补零对齐
初始化ORB时传入nfeatures参数指定单张图最多提取的关键点数量,提取描述子时,关键点不足指定数量的用零向量填充,超出的直接截断,就能得到长度统一的特征向量。
关键修正代码如下:# 超参数:单张图固定提取500个ORB关键点 FIXED_KP_NUM = 500 DESC_DIM = 32 # ORB单个描述子固定32维 orb = cv2.ORB_create(nfeatures=FIXED_KP_NUM) def get_aligned_feature(img_path): img = cv2.imread(img_path, 0) img = cv2.resize(img, (200,200)) _, des = orb.detectAndCompute(img, None) # 处理无关键点的极端情况 if des is None: des = np.zeros((FIXED_KP_NUM, DESC_DIM), dtype=np.uint8) # 补零对齐 if des.shape[0] < FIXED_KP_NUM: pad = np.zeros((FIXED_KP_NUM - des.shape[0], DESC_DIM), dtype=np.uint8) des = np.vstack([des, pad]) # 超长截断 else: des = des[:FIXED_KP_NUM] return des.reshape(-1) X = [] y = [] # 提取COVID类特征,标签赋值0 for f in myListC: X.append(get_aligned_feature(f'{path1}/{f}')) y.append(0) # 提取正常类特征,标签赋值1 for f in myListN: X.append(get_aligned_feature(f'{path2}/{f}')) y.append(1) # 转换为规整数组即可直接输入分类器训练 X = np.array(X) y = np.array(y) - 效果更优的传统方案:使用词袋模型(BoVW)做特征聚合
这是手工特征搭配传统分类器的标准流程:先收集所有训练图像提取到的ORB描述子,用K-Means聚类生成固定规模的视觉词典(比如聚成100~200个簇),再将每张图的描述子映射为视觉单词的统计直方图,直方图维度和聚类簇数一致,得到固定长度特征后再训练分类器,泛化性比直接补零截断的方案更好。
内容的提问来源于stack exchange,提问作者RANA
相关产品推荐
相关产品推荐

