作为一个普通的研一学生,之前没有做过图像处理的课题,只是简单接触过opencv和python,是在此方向是个纯小白。但是模式识别课程需要做一个烟雾检测的大作业,而老师只是讲orb、hog、lbp算法的原理,根本不知道怎么实现,毫无头绪。国庆节假期期间,在csdn上查阅了许多大佬的文章,终于有些进展。现分享我的些许成果,其中有很多不正之处,望见谅。
效果如下,将下图喂到test程序内可得img1所示输出。
本篇为大作业完成过程的第一篇,后续内容会陆续发布。
报告的主题是video based smoke detection,四人一组,一组交一份报告。
预先提供train、test两个数据集,内部有smoke和non两个文件夹,文件夹内有若干100*100大小的图片。
报告内容包括四部分:1、特征表示;2、降维及聚类;3、分类;4、模型评价及选择。
注:特征表示要从ORB、HOG、LBP算法中至少选择一种,还要使用CNN算法提取特征对比
(还有其他要求,不过上课时记得笔记丢失,只记得这一条)
拿到图片集后,发现图片的是这样的(部分图片)。图片大小的统一的,都是100*100,但是图片的名字命名规则缺没有规律(也可能是有我没发现的规律), 图片读取十分不方便。
全选图片后,对任一个图片重命名,图片集实现了批量图片重命名,得到如下图所示的图片集:
可以看出所有图片的名字得到统一化,接下来通过程序读取图片集就变得简单啦。
以下是代码分析
训练集内所有烟雾图片和无烟雾图片分别读取到pos_list和neg_list列表内
PosNum = 688
NegNum = 817
pos_path = 'train\\smoke_mqa\\'
neg_path = 'train\\non_mqa\\'
#导入正、负图片集
for i in range(0,PosNum):
fileName = pos_path+'yw ('+str(i+1)+').jpg'
img = cv2.imread(fileName)
pos_list.append(img)
for i in range(0,NegNum):
fileName =neg_path+'no_yw ('+str(i+1)+').jpg'
img = cv2.imread(fileName)
neg_list.append(img)
将pos_list和neg_list列表内的图片通过computeHOGs函数HOG特征提取、整合、存储到gradient_list列表内,然后根据正负图片集生产图片集标签,标签信息存储到labels列表内。
#添加图片集标签
computeHOGs(pos_list,gradient_list,wsize=(100,100))
for _ in range(len(pos_list)):
labels.append(+1)
computeHOGs(neg_list,gradient_list,wsize=(100,100))
for _ in range(len(neg_list)):
labels.append(-1)
HOG算法提取的图像形状特征:
1)灰度化处理图片;
2)采用Gamma校正法对输入图像进行颜色空间的标准化;
3)计算图像每个像素的梯度,将像素的梯度方向在0-360°平均划分为n个bins;
4)将图像换分为小的细胞单元(cell),每个cell有唯一的梯度特征向量,每个cell大小为(x,x);
5)统计每个cell的梯度特征向量,形成梯度直方图;
6)将y*y个cell组成一个正方形的块(block),一个block内所有cell的特征向量串联得到该block的HOG特征向量;
7)将所有block内所有的HOG特征向量串联得到此图片的HOG特征向量。
对于HOG算法的实现编程能力强的同学可以自己编写算法函数,显然本博主不是,所以我使用是opencv库内嵌的hog算法函数compute。依据此函数输入一张待处理的灰度图img,可以输出该图片hog特征序列。
使用compute函数前,需要预先初始化hog算法,依据训练集的图片和期望特征提取的复杂程度设定HOGDescriptor函数的入口参数。其中,winSize为感兴趣区域ROI大小,即训练集图片图片的大小;blockSize为块大小;blockStride为块移动步增量;cellSize为胞元大小;nBin为梯度方向数。
def computeHOGs(img_list,gradient_list,wsize=(100,100)): #计算roi的hog特征
hog = cv2.HOGDescriptor((100,100),(10,10),(5,5),(5,5),nBin)
# 窗口大小 块大小 快滑动增量 胞元大小 梯度方向数
for i in range(len(img_list)):
img = img_list[i]
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) #灰度化
gray = cv2.equalizeHist(gray) #直方图均衡化,防止有些图片过亮或过暗
gray = gamma_trans(gray,gram) #伽马变换
gradient_list.append(hog.compute(gray))
return gradient_list
HOG算法提取图片的HOG特征向量实现步骤为:
1)将图片分割为若干cellSize大小的cell;
2)从图片的左上角开始,提取blockSize大小block的梯度值;
3)block以blockStride为移动距离从左到右,再到下一行遍历整张图片;
4)根据nBins分割梯度值,得到hog特征向量
支持向量机(SVM)是一种监督机器学习算法,可用于分类或回归挑战。通过SVM算法可以找到离分隔超平面最近的点,确保它们离分割面的距离尽可能远。 核函数kernel利用低维的输入空间,将其转换为高维空间;即将不可分离问题转换为可分离问题。
创建svm分类器,设置svm内核、伽马变换系数和惩罚系数,如下图所示。生成模型,并保存为svm1.xml。
#训练svm
svm1 = cv2.ml.SVM_create()
svm1.setType(cv2.ml.SVM_C_SVC)
svm1.setGamma(0.5)
svm1.setC(3)
svm1.setKernel(cv2.ml.SVM_LINEAR) #线性
svm1.train(np.array(gradient_list), cv2.ml.ROW_SAMPLE, np.array(labels))
svm1.save("svm1.xml")
至此,模型训练完成。
def gamma_trans(img,gamma): #伽马变换
gamma_table = [np.power(x/255.0,gamma)*255.0 for x in range(256)]
gamma_table = np.round(np.array(gamma_table)).astype(np.uint8)
return cv2.LUT(img,gamma_table)
def computeHOGs(img_list,gradient_list,wsize=(100,100)): #计算roi的hog特征
hog = cv2.HOGDescriptor((100,100),(10,10),(5,5),(5,5),nBin)
# 窗口大小 块大小 快滑动增量 胞元大小 梯度方向数
for i in range(len(img_list)):
img = img_list[i]
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) #灰度化
gray = cv2.equalizeHist(gray) #直方图均衡化,防止有些图片过亮或过暗
gray = gamma_trans(gray,gram) #伽马变换
gradient_list.append(hog.compute(gray))
return gradient_list
def sliding_window(image, stepSize, windowSize):#滑动窗口
for y in range(0, image.shape[0], stepSize):
for x in range(0, image.shape[1], stepSize):
yield (x, y, image[y:y + windowSize[1], x:x + windowSize[0]])
#哪个维度超纲,哪个维度就显示原图
def mqa_hog_predict(mqa_img,i,z):
scale = 1
w,h = 100,100
x,y = 0,0
rectangles = []
font = cv2.FONT_HERSHEY_PLAIN
cs_num = 0
for (x, y, roi) in sliding_window(mqa_img, 10, (100, 100)):#对得到的图进行滑动窗口,(100, 40)为窗口大小,本文应取(64, 64)
if roi.shape[1] != w or roi.shape[0] != h: #判断是否超纲
continue
gray = cv2.cvtColor(roi,cv2.COLOR_BGR2GRAY)
gray = cv2.equalizeHist(gray)
gray = gamma_trans(gray,gram)
test_gradient = myHog.compute(gray)
a, res = svm1.predict(np.array([test_gradient]), flags=cv2.ml.STAT_MODEL_RAW_OUTPUT)
_, result = svm1.predict(np.array([test_gradient]))
score = res[0][0]
#if result[0][0] == 1 :#and score > my_res :
if score < my_res :
# cs_num+=1
#z+=1
rx, ry, rx2, ry2 = int(x * scale), int(y * scale), int((x+w) * scale), int((y+h) * scale)
rectangles.append([rx, ry, rx2, ry2, score])
windows = np.array(rectangles)
boxes = nms(windows,0.5)
# print(len(boxes))
# print(z,len(boxes))
if z == 1:
if len(boxes) > 8 :
for (x, y, x2, y2, score) in boxes: #
cv2.rectangle(mqa_img, (int(x),int(y)),(int(x2), int(y2)),(0, 255, 0), 1)
cv2.putText(mqa_img, "%f" % score, (int(x),int(y)), font, 1, (0, 255, 0))
cv2.imshow("img"+str(i), mqa_img) #显示图像,常与下一函数连用,否则图片一闪而过
return score
PosTestNum = 552
NegTestNum = 831
m1,m2=0,0
for i in range(0,NegTestNum):
fileName = 'test\\non_mqa\\csw ('+str(i+1)+').jpg'
# 导入正样本图片
img = cv2.imread(fileName)
m1=mqa_hog_predict(img,i,0)
mysj.append(m1)
#画图
plt.hist(mysj,bins=(100))
plt.title("label1")
plt.show()
for i in range(0,PosTestNum):
fileName = 'test\\smoke_mqa\\csy ('+str(i+1)+').jpg'
# 导入负样本图片
img = cv2.imread(fileName)
m2=mqa_hog_predict(img,1000+i,0)
mysj1.append(m2)
plt.hist(mysj1,bins=(100))
plt.title("label2")
plt.show()
由train.py生成的模型,利用test.py对测试集的图片进行测试,生成以下两张柱状图。label1为使用无烟雾图片得到的相似度分布图,小于0部分为二分类混淆矩阵的FP(假正例)部分,大于0为TN(正反例)部分。label3为使用烟雾图片得到的相似度分布图,小于0部分为二分类混淆矩阵的TP(真正例)部分,大于0为FN(假反例)部分。
由两图可知检测程序的实现效果不太理想,主要原因为训练集内图片太少、hog算法理解浅薄、hog算法自身的局限。
这作业对我这种既不研究图像识别,又不研究机器学习的一般人太难了,课题组的师兄师姐们去年的作业也不是烟雾检测,不能借鉴往年作业。越做越感觉任务量越巨大,做到现在还没完成总任务的四分之一,只是完成第一条和第三条作业要求的hog算法,任重而道远啊。对于作业的第二条——降维和聚类,现阶段正在学习,等到学完相关课程之后再一一加入。对于作业的第四条——模型评价及选择,经过课程学习后,本人觉得应该放在最后完成,故待前三条完成后攻克。
下一步计划打算使用LBP算法进行特征提取和分类,将会在下一篇文章分享,敬请期待。=========================================================================
本篇借鉴了以下博客内容,如有侵权,立删。
HOG算法的实现 - tangjunjun - 博客园 (cnblogs.com)
(6条消息) opencv-python 入门实战:传统方法Hog+svm实现目标检测_weixin_44689486的博客-CSDN博客
(6条消息) Python+OpenCV+HOG+SVM+行人检测_nsh119的博客-CSDN博客