目录
前言:
1、机器学习
1.1 kNN算法
1.2 SVM算法(支持向量机)
1.3 k均值聚类算法
2、深度学习
2.1 基于深度学习的图像识别
2.2 基于深度学习的对象检测
总结:
机器学习(ML)是人工智能的核心,研究如何让计算机模拟和学习人类行为。深度学习(DL)是机器学习的一个热门研究方向,主要研究样本数据的内在规律和表示层次,让计算机能够像人类一样具有分析和学习能力,识别文字、图像和声音等数据。
Opencv的机器学习模块(ml)实现了与机器学习有关的类和相关函数。本次学习其中的k最近邻(k-Nearest Neighbours,kNN)、支持向量机(Support Vector Machines,SVM)和k均值聚类(k-Means Clustering)等算法的使用。
kNN算法将找出k个距离最近的邻居作为目标的同一类别。
①图解kNN算法
步骤:首先调用cv2.ml.KNearest_create()函数创建kNN分类器;然后将训练数据和标志作为输入,调用kNN分类器的train()方法训练模型;将待分类数据作为输入,调用kNN分类器的findNearest()方法找出k个最近邻居,返回分类结果的相关信息。
代码示例:在图像中随机选择30个点,为每个点随机分配标志(0或1);图像中用矩形表示标志0,用三角形表示标志1;再随机新增一个点(圆点),用kNN算法找出其邻居,并确定其标志(完成分类)
import cv2
import numpy as np
import matplotlib.pyplot as plt
points=np.random.randint(0,100,(30,2)) #随机选择30个点
labels=np.random.randint(0,2,(30,1)) #为随机点随机分配标签(参数2表示两种标签,矩形0,三角形1)
label0s=points[labels.ravel()==0] #分出标志为0的点
plt.scatter(label0s[:,0],label0s[:,1],80,'b','s') #将标志为0的点绘制成蓝色矩形
label1s=points[labels.ravel()==1] #分出标志为1的点
plt.scatter(label1s[:,0],label1s[:,1],80,'r','^') #将标志为1的点绘制成红色三角形
newpoint=np.random.randint(0,100,(1,2)) #随机选择一个点,下面确定其分类
plt.scatter(newpoint[:,0],newpoint[:,1],80,'g','o') #将待分类的新点绘制成绿色圆点
plt.show()
#进一步使用kNN算法确认待分类新点的类别、3个最近邻居的距离
knn=cv2.ml.KNearest_create() #创建kNN分类器
knn.train(points.astype(np.float32),cv2.ml.ROW_SAMPLE,
labels.astype(np.float32)) #训练模型
ret,results,neighbours,dist=knn.findNearest(newpoint.astype(np.float32),3) #找出3个最近邻居
print("新点标志:%s"%results)
print("邻居:%s"%neighbours)
print("距离:%s"%dist)
选择得三个最近邻居是两个矩形,一个三角形。把新点归类为标签0,因为它离那个矩形最近。
②kNN算法实现手写数字识别
digits.png是一个手写数字图像,大小2000x1000,其中每个数字大小为20x20,每个数字得样本有500个(5行x100列),10个数字共5000个数字样本。可用这些数字图像训练kNN模型和执行测试。
#kNN算法实现手写识别
import cv2
import numpy as np
import matplotlib.pyplot as plt
gray=cv2.imread('digits.png',0)
digits=[np.hsplit(r,100) for r in np.vsplit(gray,50)] #分解数字:50行,100列
np_digits=np.array(digits) #转换为numpy数组
#准备训练数据,转换为二维数组,每个图像400个像素
train_data=np_digits.reshape(-1,400).astype(np.float32)
train_labels=np.repeat(np.arange(10),500)[:,np.newaxis] #定义标志
knn=cv2.ml.KNearest_create() #创建kNN分类器
knn.train(train_data,cv2.ml.ROW_SAMPLE,train_labels) #训练模型
#用绘图工具创建手写数字5图像(大小20x20)进行测试(黑白二值图像)
test=cv2.imread('d5.png',0) #打开测试图像
test=cv2.resize(test,(20,20)) #转换为20x20大小
test_data=test.reshape(1, 400).astype(np.float32) #转换为测试数据
ret,result,neighbours,dist=knn.findNearest(test_data,k=3) #执行测试
print(result.ravel())
print(neighbours.ravel())
#拍摄图像数字3,进行测试(非黑白二值图像)
img2=cv2.imread('d3.png',0)
img2=cv2.resize(img2, (20,20))
ret,img2=cv2.threshold(img2,150,255,cv2.THRESH_BINARY_INV) #反二值化阈值处理
test_data=img2.reshape(1, 400).astype(np.float32) #转换为测试数据
ret,result,neighbours,dist=knn.findNearest(test_data,k=3) #执行测试
print(result.ravel())
print(neighbours.ravel())
绘图工具创建的数字5可以直接将其灰度图像转换为测试数据,无像素丢失,k=3时识别准确率是10%;而拍照的数字3还需要先阈值处理,再转换为测试数据,会导致像素丢失,k=3时识别准确率是2/3。
可使用一条直线将线性可分离的数据分为两组,这组直线在SVM算法中称为“决策边界”;非线性可分离的数据转换为高维数据后称为线性可分离数据。这是SCV算法的理论基础。
①图解SVM算法
代码示例:在图像中选择了5个点,分为两类,类别分别是0和1。将5个点和标志作为已知分类数据训练SVM模型;然后用模型对图像中的所有点进行分类,根据分类结果设置图像颜色,从而直观显示图像像素的分类结果。
import cv2
import numpy as np
import matplotlib.pyplot as plt
#准备训练数据,假设图像高240,宽320,在其中选择5个点
traindata=np.matrix([[140,60],[80,120],[160,110],[160,190],[240,180]],dtype=np.float32)
labels=np.array([0,0,0,1,1])#5个点,前3个点为一类标志为0;后2个点为一类,标志为1
svm=cv2.ml.SVM_create() #创建SVM分类器
svm.setGamma(0.50625) #设置相关参数
svm.setC(12.5)
svm.setKernel(cv2.ml.SVM_LINEAR)
svm.setType(cv2.ml.SVM_C_SVC)
svm.setTermCriteria((cv2.TERM_CRITERIA_MAX_ITER,100,1e-6))
svm.train(traindata,cv2.ml.ROW_SAMPLE,labels) #训练模型
img=np.zeros((240,320,3),dtype="uint8") #创建图像
colors={0:(102,255,204),1:(204,204,102)}
#用SVM分类器对图像像素进行分类,根据分类结果设置像素颜色
for i in range(240):
for j in range(320):
point=np.matrix([[j,i]],dtype=np.float32) #将像素坐标转换为测试数据
label=svm.predict(point)[1].ravel() #执行预测,返回结果
img[i,j]=colors[label[0]] #根据预测结果设置像素颜色
svm_vectors=svm.getUncompressedSupportVectors() #获得SVM向量
for i in range(svm_vectors.shape[0]): #在图像中绘制SVM向量(红色圆)
cv2.circle(img,(svm_vectors[i,0],svm_vectors[i,1]),8,(0,0,255),2)
#在图像中绘制训练数据点,类别标志0使用蓝色,类别标志1使用绿色
cv2.circle(img,(140,60),5,(255,0,0),-1)
cv2.circle(img,(80,120),5,(255,0,0),-1)
cv2.circle(img,(160,110),5,(255,0,0),-1)
cv2.circle(img,(160,190),5,(0,255,0),-1)
cv2.circle(img,(240,180),5,(0,255,0),-1)
img=cv2.cvtColor(img,cv2.COLOR_BGR2RGB) #转换为RGB格式
plt.imshow(img)
plt.show()
从图上可以看出,图像上方的3个训练点为一类,下方2个点为一类;图像中上下两种颜色的交界位置为决策边界。
②用SVM算法识别手写数据
kNN算法的例子使用像素值作为特征向量。SVM算法可使用图像的定向梯度直方图(HOG)作为特征向量对图像进行分类。
import cv2
import numpy as np
def hog(img): #定义HOG描述符函数
hog=cv2.HOGDescriptor((20,20),(8,8),#定义HOGDescriptor对象
(4,4),(8,8),9,1,-1,0,0.2,1,64,True)
hog_descriptor=hog.compute(img) #计算HOG描述符
hog_descriptor=np.squeeze(hog_descriptor) #转换为一维数组
return hog_descriptor #返回HOG描述符,144位
img=cv2.imread('digits.png',0)
digits=[np.hsplit(r,100) for r in np.vsplit(img,50)] #分解数字:50行,100列
labels=np.repeat(np.arange(10),500)[:,np.newaxis] #定义对应标志
hogdata=[list(map(hog,row)) for row in digits] #计算图像的HOG描述符
trainData=np.float32(hogdata).reshape(-1,144) #转换为测试数据
svm=cv2.ml.SVM_create() #创建SVM分类器
svm.setGamma(5.383) #设置相关参数
svm.setC(2.67)
svm.setKernel(cv2.ml.SVM_LINEAR)
svm.setType(cv2.ml.SVM_C_SVC)
svm.train(trainData,cv2.ml.ROW_SAMPLE,labels) #训练模型
#用绘图工具创建手写数字5图像(大小20x20)进行测试(黑白二值图像)
test=cv2.imread('d5.png',0) #打开测试图像
test=cv2.resize(test,(20,20)) #转换为20x20大小
test_data=hog(test)
test_data=test_data.reshape(1,144).astype(np.float32) #转换为测试数据
result=svm.predict(test_data)[1]
print('识别结果:',np.squeeze(result))
#拍摄图像数字8,进行测试(非黑白二值图像)
test=cv2.imread('d8.png',0) #打开测试图像
test=cv2.resize(test,(20,20)) #转换为20x20大小
test_data=hog(test)
test_data=test_data.reshape(1,144).astype(np.float32) #转换为测试数据
result=svm.predict(test_data)[1]
print('识别结果:',np.squeeze(result))
k均值聚类算法的基本原理:根据数据的密集程度寻找相对密集数据的质心,再根据质心完成数据分类。
①图解k均值聚类算法
代码示例:在大小是240x320的图像中选择3组数据点,为了便于说明该算法,在选择数据点时设置了坐标的随机取值范围。将所有点作为分类数据,调用cv2.kmeans()函数并用该算法进行分类;在图像中用不同颜色显示分类数据和质心。
import cv2
import numpy as np
import matplotlib.pyplot as plt
#创建聚类数据,3个类别,每个类别包含20个点
data=np.vstack((np.random.randint(10,90,(20,2)),
np.random.randint(80,170,(20,2)),
np.random.randint(160,250,(20,2))))
data=data.astype(np.float32)
#定义算法终止条件
criteria=(cv2.TERM_CRITERIA_EPS+cv2.TERM_CRITERIA_MAX_ITER,20,1.0)
#使用k均值聚类算法执行分类操作,k=3,返回结果中label用于保存标志,center用于保存质心
ret,label,center=cv2.kmeans(data,3,None,criteria,10,cv2.KMEANS_RANDOM_CENTERS)
#根据运算结果返回的标志将数据分成3组,便于绘制图像
data1=data[label.ravel()==0]
data2=data[label.ravel()==1]
data3=data[label.ravel()==2]
plt.scatter(data1[:,0],data1[:,1],color='r') #绘制第1类数据点,红色
plt.scatter(data2[:,0],data2[:,1],color='g') #绘制第2类数据点,绿色
plt.scatter(data3[:,0],data3[:,1],color='b') #绘制第3类数据点,蓝色
plt.scatter(center[:,0],center[:,1],100,['#CC3399'],'s') #绘制质点,颜色是#CC3399
plt.show()
②用k均值聚类算法量化图像颜色
代码示例:即将质心作为图像新的像素,从而减少图像中的颜色值:
import cv2
import numpy as np
img=cv2.imread('flowers.png')
cv2.imshow('img',img)
img2=img.reshape((-1,3)).astype(np.float32) #转换为nx3的浮点类型数组,n=图像像素的总数/3
#定义算法终止条件
criteria=(cv2.TERM_CRITERIA_EPS+cv2.TERM_CRITERIA_MAX_ITER,10,1.0)
K=4
ret,label,center=cv2.kmeans(img2,K,None,criteria,10,cv2.KMEANS_RANDOM_CENTERS)
center=np.uint8(center) #将质心转换为整型
img3=center[label.ravel()] #转换为一维数组
img3=img3.reshape((img.shape)) #恢复为原图像数组形状
cv2.imshow('K=4',img3)
cv2.waitKey(0)
cv2.destroyAllWindows()
机器学习通常包含输入、特征提取、分类和输出4个步骤。深度学习通常分为输入、特征提取与分类和输出3个步骤,它将机器学习中的特征提取和分类合并在同一个步骤中完成。相对于机器学习,深度学习需要提供的输入数据量更大,计算量也更大,“深度”体现在神经网络层次规模上,例如, ResNet 及其变种实现的神经网络多达上百层。
OpenCV支待目前流行的深度学习框架 包活 Cafe、TensorFlow 和Torch/Pytorch等,以及基于开放神经网络交换(Open Neural Network Exchange, ONNX)。在应用程序中,只需要导入预练模型,即可用准备好的数据执行预测操作,获得需要的处理结果。
图像识别是将图像内容作为一个对象来识别其类型。使用opencv中的深度学习预训练模型进行图像识别步骤:首先从配置文件和预训练模型文件中加载模型;将图像文件处理为块数据(blob);将图像文件的块数据设置为模型输入;执行预测;处理预测结果。(与2.2的目标检测步骤一致)
①基于AlexNet和Caffe模型的图像识别
AlexNet网络包含了5层卷积神经网络(CNN),3层全连接网络,采用GPU(显卡,即图像处理器)来加速计算,处理时使用的图像块大小是224x224。
Caffe的全称是快速特征嵌入的卷积结构,是一个兼具表达性、速度和思维模块化的深度学习框架。内核用C++实现,并提供了Python和Matlab等接口。
下面是基于AlexNet和Caffe模型的图像识别示例代码:
import cv2
import numpy as np
import matplotlib.pyplot as plt
from PIL import ImageFont,ImageDraw,Image
#读取文本文件中的类别名称,共1000种类别,每行为一个类别,从第1个字符开始为名称
file=open('classes.txt')
names=[r.strip() for r in file.readlines()]
file.close()
classes=[r[0:] for r in names] #获取每个类别的名称
#从文件中载入Caffe模型
net=cv2.dnn.readNetFromCaffe("AlexNet_deploy.txt","AlexNet_CaffeModel.dat") #缺少模型文件???
image=cv2.imread('ta.png') #打开图像,用于识别分类
#创建图像块数据,大小是(224,224),颜色通道的均值缩减比例因子为(104,117,123)
blob=cv2.dnn.blobFromImage(image,1,(224,224),(104,117,123))
net.setInput(blob) #将图像块数据作为神经网络输入
#执行预测,返回结果是一个1x1000的数组,按顺序对应1000种类别的可信度
result=net.forward()
ptime,x=net.getPerfProfile() #获得完成预测时间
print('完成预测时间:%.2f ms'%(ptime*1000.0/cv2.getTickFrequency()))
sorted_ret=np.argsort(result[0]) #将预测结果按可信度由高到低排序
top5=sorted_ret[::-1][:5] #获得排名前5的预测结果
ctext="类别:"+classes[top5[0]]
ptext="可信度:{:.2%}".format(result[0][top5[0]])
#输出排名前5的预测结果
for (index,idx) in enumerate(top5):
print("{}.类别,可信度:{:.2%}".format(index+1,classes[idx],result[0][idx]))
#在图像中输出排名第1的预测结果
fontpath="STSONG.TTF"
font=ImageFont.truetype(fontpath,80) #载入中文字体,设置字号
img_pil=Image.fromarray(image)
draw=ImageDraw.Draw(img_pil)
draw.text((10,10),ctext,font=font,fill=(0,0,255)) #绘制文字
draw.text((10,100),ptext,font=font,fill=(0,0,255))
img=np.array(img_pil)
img=cv2.cvtColor(img,cv2.COLOR_BGR2RGB)
plt.imshow(img)
plt.axis("off")
plt.show()
缺少模型文件,暂时无输出结果!
②基于ResNet和Caffe模型进行图像识别
ResNet全称是深度残差网络,主要特点是神经网络中增加了残差单元,可通过残差学习解决网络深度增加带来的退化问题,提高预测准确率。
代码只需要改变加载的模型文件即可:
net=cv2.dnn.readNetFromCaffe("ResNet_50_deploy.prototxt","resnet-18.caffemodel") #缺少模型文件???
对象检测是指检测出图像中的所有对象,并识别对象的类型。步骤与图像识别一致。
①基于MobilNet_SSD和Caffe的预训练模型进行对象检测
MobileNet是一种用于移动和嵌入式视觉应用的高效模型,它使用深度可分离卷积来构建轻型深度神经网络。
SSD全称是单发多盒检测器,是一种对象检测算法。MobilNet_SSD结合了MobilNet和SSD的特点。
import cv2
import numpy as np
import matplotlib
import matplotlib.pyplot as plt
from PIL import ImageFont,ImageDraw,Image
fontpath="STSONG.TTF" #加载字体以便显示汉字
font=ImageFont.truetype(fontpath,20)
font2={'family':'STSONG',"size":22}
matplotlib.rc('font',**font2) #设置plt字体
#准备对象名称类别
objects_names=('背景','飞机','自行车','鸟','船','瓶子','公共汽车','小汽车','猫','椅子',
'牛','餐具','狗','马','摩托车','人','盆栽','羊','沙发','火车','监视器')
mode=cv2.dnn.readNetFromCaffe("MobileNetSSD_deploy.txt",#从文件中加载预训练的模型???
"MobileNetSSD_Caffemodel.dat")
image=cv2.imread("object1.png")
blob=cv2.dnn.blobFromImage(image,0.007843,(224,224),(120,120,127)) #创建图像的块数据
mode.setInput(blob) #将图像块数据设置为模型输入
result=mode.forward() #执行预测
ptime,x=mode.getPerfProfile() #获得完成预测时间
title='完成预测时间:%.2f ms'%(ptime*1000.0/cv2.getTickFrequency())
for i in range(result.shape[2]): #处理检测结果
confidence=result[0,0,i,2] #获得可信度
if confidence > 0.3: #输出可信度大于30%的检测结果
a,id,a,x1,y1,x2,y2=result[0,0,i]
name_id=int(id) #获得类别名称
blob_size=280
heightScale = image.shape[0] / blob_size #计算原图像和图像块的高度比例
widthScale = image.shape[1] / blob_size # 计算原图像和图像块的宽度比例
#计算检测出对象的左下角和右上角坐标
x1 = int(x1 * blob_size * widthScale)
y1 = int(y1 * blob_size * heightScale)
x2 = int(x2 * blob_size * widthScale)
y2 = int(y2 * blob_size * heightScale)
#在图像中输出对象名称和可信度
if name_id in range(len(objects_names)):
text=objects_names[name_id] + "\n{:.1%}".format(confidence)
img_pil = Image.fromarray(image)
draw = ImageDraw.Draw(img_pil)
draw.text((x1+5, y1), text, font=font, fill=(0, 0, 255)) # 绘制文字
img = np.array(img_pil)
img=cv2.cvtColor(img,cv2.COLOR_BGR2RGB)
plt.title(title)
plt.imshow(img)
plt.axis("off")
plt.show()
缺少模型文件暂时无输出,模型的训练在学习中......
②基于YOLO和Darknet预训练模型的对象检测
YOLO是一种对象检测算法,将图像分成若干个网络,对每个网格计算边框和可信度。
Darknet是一个基于C语言与CUDA实现的开源深度学习框架,其主要特点是容易安装、移植性好、支持CPU与GPU计算。
cv2.dnn.readNetFromDarknet()函数用于从文件中加载基于YOLO V3和Darnet的预训练模型。
import cv2
import numpy as np
import matplotlib
import matplotlib.pyplot as plt
from PIL import ImageFont,ImageDraw,Image
fontpath="STSONG.TTF" #加载字体以便显示汉字
font=ImageFont.truetype(fontpath,20)
font2={'family':'STSONG',"size":22}
matplotlib.rc('font',**font2) #设置plt字体
#加载文本文件中的对象名称,共80种类别,每行为一个类别
file=open('objects_names.txt',encoding='utf-8')
objects_names=[r.strip() for r in file.readlines()]
file.close()
#从文件中加载预训练的Darknet模型
mode=cv2.dnn.readNetFromDarknet("yolo3-tiny.cfg","yolo3.weights")
image=cv2.imread("object1.png")
imgH,imgW=image.shape[:2]
out_layers=mode.getLayerNames() #获得输出层
out_layers=[out_layers[i[0]-1] for i in mode.getUnconnectedOutLayers()]
blob=cv2.dnn.blobFromImage(image,1/255.0,(416,416),#创建图像块数据
swapRB=True,crop=False)
mode.setInput(blob) #将图像快数据设置为模型输入
layer_results=mode.forward(out_layers) #执行预测,返回每层的预测结果
ptime,_=mode.getPerfProfile() #获得完成预测时间
title_text='完成预测时间:%.2f ms'%(ptime*1000.0/cv2.getTickFrequency())
result_boxes=[]
result_scores=[]
result_name_id=[]
for layer in layer_results: #遍历所有输出层
for box in layer: #遍历层的所有输出预测结果,每个结果为一个边框
#预测结果结构:x,y,w,h,confidence,80个类别的概率
probs=box[5:]
class_id=np.argmax(probs) #找到概率最大的类别名称
prob=probs[class_id] #找到最大概率
if prob > 0.5: #筛选出概率大于50%的类别
#计算每个box在原图像中的绝对坐标
box=box[0:4]*np.array([imgW,imgH,imgW,imgH])
(centerX,centerY,width,height)=box.astype("int")
x=int(centerX-(width/2))
y=int(centerY-(height/2))
result_boxes.append([x,y,int(width),int(height)])
result_scores.append(float(prob))
result_name_id.append(class_id)
#应用非最大值抑制消除重复边框,获得要挥着的box
draw_boxes=cv2.dnn.NMSBoxes(result_boxes,result_scores,0.6,0.3)
if len(draw_boxes) > 0:
for i in draw_boxes.ravel():
#获得边框坐标
(x,y)=(result_boxes[i][0],result_boxes[i][1])
(w, h) = (result_boxes[i][2], result_boxes[i][3])
#绘制边框
cv2.rectangle(image,(x,y),(x+w,y+h),(0,255,0),1)
#输出类别名称和可信度
text=objects_names[result_name_id[i]] +\
"\n{:.1%}".format(result_scores[i])
img_pil = Image.fromarray(image)
draw = ImageDraw.Draw(img_pil)
draw.text((x+ 5, y), text, font=font, fill=(0, 0, 255)) # 绘制文字
img = np.array(img_pil)
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
plt.title(title_text)
plt.imshow(img)
plt.axis("off")
plt.show()
模型的训练在学习中,缺少模型文件暂时无输出.......后续会在PyTorch框架中详细实践!
由于是初学者可能很多地方没有总结完全或者有误,后续深入学习后会不断回来该删,也欢迎各位朋友指正!通过以上几期的学习,我对于Opencv有了大概的知识架构,后续我会继续补充细节,深入学习本知识点!