一直想用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相当。
一个例子
原始图片:
可以先看看有哪些主要颜色:
>>> 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分类颜色表
下面分别用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()
##开始分析
#初始化treeArea类
lower_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)]
#treeArea的run方法一次性完成数据读入,载入训练器,进行分类的工作
#用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')
最终将得到一个包含每张图片指定颜色(区间)比例的列表,以及输出的遮罩图片,如下:
-
遮罩
-
应用遮罩的图片
后话:感觉这个分类器可以做很多事情,和爬虫结合一下,用爬虫爬取一点神秘的东西,然后用它做内容识别,每天上班前把它挂着,下班回来,就可以点开一一浏览神秘的东西了,美滋滋!