【OpenCV-Python】——机器学习kNN算法&SVM算法&k均值聚类算法&深度学习图像识别&对象检测

目录

 


前言:

机器学习(ML)是人工智能的核心,研究如何让计算机模拟和学习人类行为。深度学习(DL)是机器学习的一个热门研究方向,主要研究样本数据的内在规律和表示层次,让计算机能够像人类一样具有分析和学习能力,识别文字、图像和声音等数据。

1、机器学习

Opencv的机器学习模块(ml)实现了与机器学习有关的类和相关函数。本次学习其中的k最近邻(k-Nearest Neighbours,kNN)、支持向量机(Support Vector Machines,SVM)和k均值聚类(k-Means Clustering)等算法的使用。

1.1 kNN算法

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是一个手写数字图像,大小2000×1000,其中每个数字大小为20×20,每个数字得样本有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。

1.2 SVM算法(支持向量机)

 可使用一条直线线性可分离的数据分为两组,这组直线在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))

     

 1.3 k均值聚类算法

k均值聚类算法的基本原理:根据数据的密集程度寻找相对密集数据的质心,再根据质心完成数据分类。

①图解k均值聚类算法

代码示例:在大小是240×320的图像中选择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()

2、深度学习

机器学习通常包含输入、特征提取、分类和输出4个步骤。深度学习通常分为输入、特征提取与分类和输出3个步骤,它将机器学习中的特征提取和分类合并在同一个步骤中完成。相对于机器学习,深度学习需要提供的输入数据量更大,计算量也更大,“深度”体现在神经网络层次规模上,例如, ResNet 及其变种实现的神经网络多达上百层。

OpenCV支待目前流行的深度学习框架 包活 Cafe、TensorFlow 和Torch/Pytorch等,以及基于开放神经网络交换(Open Neural Network Exchange, ONNX)。在应用程序中,只需要导入预练模型,即可用准备好的数据执行预测操作,获得需要的处理结果。

2.1 基于深度学习的图像识别

图像识别是将图像内容作为一个对象来识别其类型。使用opencv中的深度学习预训练模型进行图像识别步骤:首先从配置文件和预训练模型文件中加载模型;将图像文件处理为块数据(blob);将图像文件的块数据设置为模型输入;执行预测;处理预测结果。(与2.2的目标检测步骤一致)

①基于AlexNet和Caffe模型的图像识别

AlexNet网络包含了5层卷积神经网络(CNN),3层全连接网络,采用GPU(显卡,即图像处理器)来加速计算,处理时使用的图像块大小是224×224。

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")  #缺少模型文件???

2.2 基于深度学习的对象检测

对象检测是指检测出图像中的所有对象,并识别对象的类型。步骤与图像识别一致。

①基于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有了大概的知识架构,后续我会继续补充细节,深入学习本知识点!

文章出处登录后可见!

已经登录?立即刷新

共计人评分,平均

到目前为止还没有投票!成为第一位评论此文章。

(0)
社会演员多的头像社会演员多普通用户
上一篇 2023年5月23日
下一篇 2023年5月23日

相关推荐

此站出售,如需请站内私信或者邮箱!