用python的opencv写了个找绿色的东西

Posted by rogerclarkgc on 周五 16 十二月 2016

一直想用python或者R来实现一个辨识颜色的程序,最近研究确实也需要这么一个东西,于是动了opencv的脑筋,发现这个库实在强大,我用python拼凑一番,把里面的有用的东西写成了一个类,专门用来找绿色的东西,其实就是找图片中的绿色。

写来写去,发现代码从一开始的几十行,到几百行,类里面的方法、属性也越来越多,估计过了一周,我自己都会忘了这个类怎么用,所以写一发blog,趁自己脑子清楚时至少把使用方法记录下来。

treeArea 类属性方法表:

该类有如下方法和属性

['__doc__', '__init__', '__module__', 'afterList',

'calcGreenArea', 'calcMask', 'fileList', 'knnMask',

'lower', 'maskList', 'readFiles', 'result', 'run',

'savePic', 'showPic', 'svmMask', 'type', 'upper']

方法参数表:

__init__(self, lower, upper, pictype)

lower:颜色分量下限值,用于简单分类器

upper:颜色分量上限值,用于简单分类器

pictype:记录载入图像类型的变量

__init__方法内部还初始化了:

self.fileList, self.maskList, self.afterList, self.result,

四个空List对象用于存储读入的文件、处理的遮罩、处理后图像、计数结果。

readFiles(self, fileName bgr2hsv = 1):

fileName:列表对象,读入的文件名

bgr2hsv:转换颜色空间,有时会使分类效果更好

该方法读入文件,并转换文件色域至HSV空间

showPic(self, fileName, select = "pic")

fileName:需要显示的文件名字

select:需要显示的图像类型,"pic"原图,"mask"遮罩,"res"应用遮罩的图像

savePic(self, fileName, select = "mask")

showPic()类似,存储图像

calcMask(self, paraset = [0, 1, 0])

paraset:参数列表,第一个参数控制cv2.medianBlur()函数是否应用到遮罩,第二个参数控制cv2.morphologyEx()函数是否应用到遮罩,第三个参数控制是否计算应用遮罩后的图像。

该方法为简单分类器,通过输入的颜色上限和下限对图像中颜色进行二分类, 默认开启cv2.morphologyEx()对遮罩进行开运算去除噪点,cv2.medianBlur()也用于去除噪点,但效果不如前者

knnMask(self, knnClassifier)

knnClassifier:训练好的一个KNN分类器

该方法基于openCV的K近邻机器学习方法来分辨图像中绿色植被区域,准确率较高,但耗时较长(训练集合越大,耗时越久)。对绿色水体误识别较高

默认开启中值滤波来消除噪点,平滑边缘。

svmMask(self, svmClassifier)

svmClassifier:一个训练好的SVM分类器

该函数方法基于openCV的支持向量机机器学习方法来分辨图像中绿色植被,准确率稍低,但运行速度快,对绿色水体、阴影、绿色操场误识别较高。

默认开启中值滤波

calcGreenArea(self, shape = "rectangle")

shape:研究区域的形状,默认为矩形,'circle'为圆形

该函数方法利用已经计算好的遮罩文件,对遮罩中大于零的像素点记数(标记为绿色植被),最后除以总像素数,得到植被覆盖比例。

run(self, fileName, typeof = 0, classifier = 0)

fileName:读取的文件名

typeof:选择分类器,"bitwise"简单分类器,"knn"knn分类器,"svm"svm分类器

classifier:当使用knn或svm时,需要传入对应的分类器

该函数方法用于快捷的计算图像中的绿色植被区域,整合读取、计算遮罩、统计绿色植被区域几个函数的功能

knnClassifier 类属性方法表:

该类有如下属性和方法:

['__doc__', '__init__', '__module__', 'autoSelect',

'knn', 'readFile', 'save', 'test', 'testData',

'testName', 'train', 'trainData', 'trainName']

方法参数表

__init__(self, trainName, testName, save = 0)

trainName:用于训练的文件列表

testName:用于测试的文件列表

save:暂时没用,计划用于存储训练数据和测试数据

该方法内部还有 self.testData self.trainData self.knn 三个对象,分别是训练数据和测试数据空列表(均为0行3列),初始化的knn分类器

autoSelect(self, datatype = 1, bgr2hsv = 1)

datatype:选择数据类型,1为训练数据,2为测试数据

bgr2hsv:转换颜色空间

使用kmeans方法精简训练集和测试集,每个样本集(图片)提取30个不同的颜色特征。

readFile(self, datatype = 1, bgr2hsv = 1)

datatype:选择数据类型,1为训练数据,2为测试数据

bgr2hsv:转换颜色空间,默认开启

该函数用于为knn分类器读入训练数据和测试数据,训练数据和测试数据都是12*12pixel的图片(可大可小,边长同的偶数),并且要求一半阳性样本一半阴性样本

train(self)

训练分类器,内含一个trainLabels对象,为训练数据的行数*1列的np.array对象,一半1一半0

test(self)

测试分类器,使用训练好的knn分类器对测试数据进行测试,在测试中会利用tqdm模块的进度条显示实时进度和测试时间,最后显示测试准确率。

svmClassifier 类基本与knnClassifier类一致,使用方法无区别

开发中的功能:

计划增加一个分类器类,logistic分类器,类似SVM,产生一个决策边界来划分类别,比knn应该更快速,目前开发完成的函数方法:

sigmoid(x)

sigmoid函数,分类器的核心

mapFeature(X1, X2, X3, degree = 1)

特征映射函数,将3维的特征向量按照多项式映射到更高维的空间,以加大各个训练数据之间的间隔

costFunctionReg(theta, X, labels, lamd)

计算logistic分类器对应的代价函数,lamd为正则项,用于防止过拟合

costFunctionReg(theta, X, labels, lamd)

计算代价函数在各个特征分量的上的梯度,用于后续寻找代价函数的最小值

predict(theta, X)

根据theta(描述决策边界的向量)来预测分类。

kmeanColor(img, K=30, save = 0)

利用kmeans分类输出图像中主要颜色,可视化结果,将输出包含K种颜色的彩色条带到屏幕

目前问题:利用scipy.optimize中fmin_bfgs()函数来帮助求取代价函数最小值出现WARNING:log() encounter by zero错误,可能出现了意外的精度损失,但仍然有结果,其结果准确度在88%,与SVM相当。

一个例子

原始图片:

origin

可以先看看有哪些主要颜色:

    >>> kmeanColor('32.jpg', save = 1)
   array([[215, 173, 143],
   [229, 187, 140],
   [136, 104,  79],
   [197, 123,  52],
   [246, 199, 132],
   [164, 129,  97],
   [243, 172,  76],
   [229, 154,  70],
   [180, 149, 123],
   [238, 187, 127],
   [243, 182,  96],
   [106,  82,  70],
   [161, 110,  59],
   [213, 155,  97],
   [239, 202, 156],
   [ 91,  54,  34],
   [209, 143,  76],
   [250, 245, 238],
   [216, 164, 115],
   [246, 228, 208],
   [202, 132,  62],
   [225, 176, 122],
   [238, 179, 111],
   [218, 154,  86],
   [188, 113,  45],
   [118,  79,  46],
   [215, 140,  59],
   [222, 167, 106],
   [243, 215, 179],
   [231, 166,  93]], dtype=uint8)

得到的kmean分类颜色表

kmean

下面分别用svm和knn把里面的妹子提取出来

使用treeindentify.py的所有功能需要做这些事情:

from treeindentify import treeArea
from treeindentify import knnClassifier
from treeindentify import svmClassifier
from treeindentify import mapFeature
from treeindentify import sigmoid, costFunctionReg, costFunctionGrad, kmeanColor

下面进行一个完整的分析流程:

##读取数据,用于训练分类器
#训练用数据
fileName = ["green"+ str(x) + ".jpg" for x in range(1, 7)]
#测试用数据
testName = ["test" + str(y) + ".jpg" for x in range(1, 11)]
#初始化knn分类器
knn = knnClassifier(fileName, testName, save = 0)
#用autoSelect方法精简训练集,减少特征空间,加快速度
knn.autoSelect(datatype = 1, bgr2hsv = 0)
#读取测试集
knn.readFile(datatype = 0, bgr2hsv = 0)
#进行训练和测试
knn.train()
knn.test()
#svm分类器用法和knn分类器一样
svm = svmClassifier(fileName, testName, save = 0)
svm.autoSelect(datatype = 1, bgr2hsv = 0)
svm.readFile(datatype = 0, bgr2hsv = 0)
svm.train()
svm.test()

##开始分析
#初始化treeArealower_tree = np.array([30, 105, 35])
upper_tree = np.array([60, 205, 100])
test = treeArea(lower_tree, upper_tree, pictype)
#准备读入和存储的文件名
pictype = ".jpg"
fileName = [str(x) + pictype for x in range(1, 28)]
saveName = [str(x) + "_svm_knnbj" + ".jpg" for x in range(1, 28)]
#treeArearun方法一次性完成数据读入,载入训练器,进行分类的工作
#用svm分类器分类
test.run(fileName, typeof = 'svm', classifier = svm)
#用knn分类器分类
test.run(fileName, typeof = 'knn', classifier = knn)
#用treeArea自带的简单分类器分类
test.run(fileName, typeof = 'bitwise')
#存储分类结果
test.savePic(saveName, select = 'mask')
test.svaePic(saveName, select = 'res')

最终将得到一个包含每张图片指定颜色(区间)比例的列表,以及输出的遮罩图片,如下:

  • 遮罩 mask

  • 应用遮罩的图片

res

后话:感觉这个分类器可以做很多事情,和爬虫结合一下,用爬虫爬取一点神秘的东西,然后用它做内容识别,每天上班前把它挂着,下班回来,就可以点开一一浏览神秘的东西了,美滋滋!