使用OpenCV中的分类器和颜色识别的苹果位置识别

使用OpenCV中的分类器和颜色识别的苹果位置识别

    • 环境配置和安装
    • 拍摄并处理样本
    • 生成正样本的描述文件pos.vec
    • 开始训练分类器
    • 单目测距
    • 完成分类器与颜色识别,轮廓提取算法的结合
    • 运行结果图片展示
    • 我的代码,训练分类器的文件夹下载

这是我本科毕业设计中的软件项目,看到目前本博客中还暂无既使用OpenCV级联分类器又使用颜色是被边缘检测的项目,本来想对目标水果——苹果做实例分割的,迫于硬件条件唯数不多的计算资源被研究生占用了,无奈只好使用OpenCV级联分类器和颜色识别边缘检测来模拟实例分割并输出苹果的坐标。

废话少说让我们开始吧。
.
.

环境配置和安装

.
.
OpenCV :需要安装文件夹中有 训练样本.exe和 训练级联分类器.exe两个文件的版本,我安装的 OpenCV 4.5.2版本有这两个文件,此版本在https://opencv.org/releases/网站去下载
编译器使用 Pycharm 2021.1.1 (Pycharm的版本影响不大,Jetbrain官网下最新的就行)
在Pycharm中Package中安装必要的Python包,进入Pycharm中Python Interpreter,安装下图中的包:使用OpenCV中的分类器和颜色识别的苹果位置识别_第1张图片

去官网下载到opencv-3.4.14-xxxxxx.exe后安装到D://opecv即可,随后在D目录下新建一个文件夹用于后续训练分类器的样本存储,模型存储等,我的训练文件夹命名为train_example,随后将D:/opencv/build/x64/vc15/bin文件夹中的
opencv_createsamples.txt和 opencv_traincascade.exe文件拷贝到D:/train_example文件夹内,
并在该文件夹中新建3个空文件夹:posdata , negdata ,xml
分别用来存储训练的正样本图片集,负样本图片集和训练出来的模型。
使用OpenCV中的分类器和颜色识别的苹果位置识别_第2张图片

准备工作到此结束。
.

拍摄并处理样本

.
Opencv中自带的级联分类器是opencv自带的一个训练好的,用于识别人脸的模型。但是opencv留了两个.exe文件用于给用户训练自己的识别模型。

1)正样本的拍摄:我的项目是识别苹果,所以需要拍摄大约1000张各个场景下的苹果的照片
使用QQ影音,图片工厂,或者Python脚本来处理样本图片为40*40,灰度化,并从1开始命名。
使用OpenCV中的分类器和颜色识别的苹果位置识别_第3张图片
在文件夹内运行cmd,输入dir /b>pos.txt,生成所有文件的目录索引。
新建一个Python脚本,给生成的pos.txt文件每一行后面加上 1 0 0 40 40,意义为:1代表文件,0 0 40 40代表从(0,0)到(40,40)读取图片,处理文件的Python脚本如下:

import os
import numpy as np

with open("D:\\train_example\\posdata\\pos.txt") as txt:
    content = txt.readlines()  # 读全部行
    txt.close()
lines = np.array(content)  # 转换成array 类型
num_of_instances = lines.size  # 整个txt的行数
print("number of instances: ", num_of_instances)
list = []
for i in range(0, num_of_instances):
    name, label = lines[i].split("\n")
    list.append(name + " 1 0 0 40 40\n" + label)  # 统一每行加字符串"_aligned.
with open("D:\\train_example\\posdata\\pos1.txt", 'w')as F:
    F.writelines(list)  # 写入到另一个txt文件中
    F.close()

2)负样本的拍摄训练:任何画面中不包含识别物体的图片都可以作为训练的负样本。
我业余爱好摄影,直接从相机卡里考出大约3000张图片即可作为负样本,使用图片工厂,QQ图像等软件把图片分辨率降为500*500以下
使用OpenCV中的分类器和颜色识别的苹果位置识别_第4张图片
同样,在该文件夹内使用dir /s/b >neg.txt,生成负样本中每个图片的路径
使用OpenCV中的分类器和颜色识别的苹果位置识别_第5张图片

.

生成正样本的描述文件pos.vec

.
在项目文件夹(即D:/train_example目录下)运行cmd

opencv_createsamples.exe -vec pos.vec -info posdata\pos.txt -bg negdata\neg.txt -w 40 -h 40 -num 1000

将会在项目文件夹目录内生成pos.vec描述文件,并且告诉系统正样本集的目录和负样本的目录文件 posdata.txt与negdata.txt, 图片尺寸40*40像素,数量1000

.
.

开始训练分类器

.
.
在项目文件夹中(D://train_example文件夹下)运行cmd
输入

opencv_traincascade.exe -data xml -vec pos.vec -bg negdata\neg.txt -numPos 900 -numNeg 3000 -numStages 20 -w 40 -h 40 -minHitRate 0.9999 -maxFalseAlarmRate 0.5 -mode ALL

接下里就等着运行结果吧,别待机电脑
使用OpenCV中的分类器和颜色识别的苹果位置识别_第6张图片
训练完成,接下来可以试试训练的结果如何,在Pycharm中新建一个train_test.py文件
用以下代码测试下分类器的分类效果:

import cv2
# 加载训练好的分类器
faceCascade = cv2.CascadeClassifier("cascade.xml")
faceCascade.load('D:\\train_example\\xml\\cascade.xml')
 
cap = cv2.VideoCapture(0)
while True:
    ret, frame = cap.read()
    img = frame.copy()
    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
    rect = faceCascade.detectMultiScale(
        gray,
        scaleFactor=1.15,
        minNeighbors=3,
        minSize=(3,3),
        flags = cv2.IMREAD_GRAYSCALE
    )
    for (x, y, w, h) in rect:
        cv2.rectangle(frame, (x, y), (x+w, y+h), (0, 255, 0), 2)
 
    cv2.imshow('frame', frame)
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break
cap.release()
cv2.destroyAllWindows()

正常的话,对视频内的目标结果如下:
使用OpenCV中的分类器和颜色识别的苹果位置识别_第7张图片
.
.

单目测距

.
.
由于本毕设项目需要获得苹果的三维坐标,还差一个苹果到摄像头的距离,由于使用了单相机,无法通过视差获取深度,原理为小孔成像的相似三角形,即需要知道自己使用的相机单个像素大小,详情可以见本CSDN博客:

https://blog.csdn.net/ikoiiii/article/details/85219059?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522162616414516780265454688%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id=162616414516780265454688&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2allfirst_rank_v2~rank_v29-2-85219059.pc_search_result_cache&utm_term=opencv%E5%8D%95%E7%9B%AE%E6%B5%8B%E8%B7%9D&spm=1018.2226.3001.4187

那么我的相机是1/2.3的底,查上述博客的表中得到单个像素是0.0008厘米,焦距写在相机外壳上为6mm,苹果的大小(直径)大约为60mm-80mm,我计算距离时取得是70mm。
.
.

完成分类器与颜色识别,轮廓提取算法的结合

.
.
对于复杂的画面,直接使用颜色识别,轮廓识别,会出现很多错误的目标,不妨使用分类器先将目标区域识别出来,再对目标区域进行颜色过滤,轮廓识别,获取到多个目标的轮廓再使用Circle()函数把获取到的目标框起来获得圆心的坐标。

import cv2
import numpy as np
faceCascade = cv2.CascadeClassifier("cascade.xml")# 加载训练好的分类器
faceCascade.load('D:\\train_example\\xml\\cascade.xml')
unit_pixel=0.0008
f=0.6
red_lower = np.array([0, 10, 50])
red_upper = np.array([20, 255, 255]) #设置过滤出苹果颜色的两个阈值,色彩模型是HSV 不是RGB
cap = cv2.VideoCapture(1) #外接相机为1,电脑自带相机为0,多接几个相机数字可以自己调
while True:
    ret, frame = cap.read()
    ROI = np.zeros(frame.shape, np.uint8)
    img = frame.copy()
    data = np.array([[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],])

    # cv2.putText(draw2, 'biggest_apple_distance:', (0, 20), cv2.FONT_HERSHEY_COMPLEX, 1, (0, 255, 0), 2)
    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
    rect = faceCascade.detectMultiScale(
        gray,
        scaleFactor=1.15,
        minNeighbors=3,
        minSize=(3, 3),
        flags=cv2.IMREAD_GRAYSCALE
    )
    for (x, y, w, h) in rect:
        cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 255, 0), 3)
        cv2.rectangle(ROI, (x,y), (x+w,y+h), (255, 255, 255), thickness=-1)
    imgroi = ROI & img #将分类器获取到的目标的矩形框内容保留,画面中剩下的
    # cv2.putText(frame, 'Target', (target_x, target_y), cv2.FONT_HERSHEY_COMPLEX, 1, (0, 0, 0), 2)
    hsv = cv2.cvtColor(imgroi, cv2.COLOR_BGR2HSV)
    # 设置阈值,去除背景 保留所设置的颜色
    mask = cv2.inRange(hsv, red_lower, red_upper)
    # ret, thresh = cv2.threshold(dilation, 150, 255, cv2.THRESH_BINARY)  # 阈值处理 二值化 用150不用255是因为有些细胞就直接变白
    thresh1 = cv2.GaussianBlur(mask, (3, 3), 0)  # 高斯滤波
    contours, hierarchy = cv2.findContours(thresh1, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
    contours1=[]
    for i in contours:
        # area.append(cv2.contourArea(i))
        # print(area)
        if cv2.contourArea(i) > 10000:  # 计算面积 去除面积小的 连通域
            contours1.append(i)
        # 以上使用面积去除小面积连通域, 参考CSDN中显微镜细胞项目
        
        # 以下是使用线条长度进行筛选
    min_size = 200

    delete_list = []

    for i in range(len(contours1)):
        if (cv2.arcLength(contours1[i], True) < min_size):
            delete_list.append(i)

    k=0
    for cont in contours1:

        (x,y), radius = cv2.minEnclosingCircle(cont)

        data[0][k]=radius
        data[1][k]=int(x)
        data[2][k]=int(y)
        cv2.circle(imgroi,(int(x),int(y)),int(radius),(0,255,255),10)
        k=k+1
    data.T[np.lexsort(data[::-1, :])].T


    # print("最大的苹果的距离为:%d" % banjin[0])
    width=data[0][0]*unit_pixel
    distance=7*f/width
    target_x=data[1][0]
    target_y=data[2][0]

    # for i, j in zip(contours, range(len(contours))):
    #     M = cv2.moments(i)
    #     cX = int(M["m10"] / M["m00"])
    #     cY = int(M["m01"] / M["m00"])
    #     draw = cv2.putText(imgroi, str(j + 1), (cX, cY), 5, 5, (255, 0, 255), 5)

    draw2 = cv2.circle(imgroi.copy(), (target_x, target_y), data[0][0], (255, 255, 255), -1)
    cv2.line(draw2, (0, target_y), (target_x, target_y), (255, 0, 0), 3)
    cv2.line(draw2, (target_x, 0), (target_x, target_y), (255, 0, 0), 3)
    cv2.line(draw2, (target_x, target_y), (640, target_y), (255, 0, 0), 3)
    cv2.line(draw2, (target_x, target_y), (target_x, 480), (255, 0, 0), 3)
    cv2.putText(draw2, 'biggest_apple_distance:', (0, 20), cv2.FONT_HERSHEY_COMPLEX, 1, (0, 255, 0), 2)
    cv2.putText(draw2, 'And_its_coordinate_is::', (0, 90), cv2.FONT_HERSHEY_COMPLEX, 1, (0, 255, 0), 2)
    cv2.putText(draw2, 'Target', (target_x, target_y), cv2.FONT_HERSHEY_COMPLEX, 1, (0, 0, 0), 2)
    distance_display = "% .2f cm" % distance
    target_x_display = "% .0f , " % target_x
    target_y_display = "% .0f " % target_y
    cv2.putText(draw2, distance_display, (40, 60), cv2.FONT_HERSHEY_SIMPLEX, 0.75, (0, 0, 255), 2)
    cv2.putText(draw2, target_x_display, (40, 120), cv2.FONT_HERSHEY_SIMPLEX, 0.75, (0, 0, 255), 2)
    cv2.putText(draw2, target_y_display, (120, 120), cv2.FONT_HERSHEY_SIMPLEX, 0.75, (0, 0, 255), 2)
    # cv2.putText(draw2, target_x_display, (half_target_x, target_y), cv2.FONT_HERSHEY_SIMPLEX, 0.75, (0, 0, 255), 2)
    # cv2.putText(draw2, target_y_display, (target_x, half_target_y), cv2.FONT_HERSHEY_SIMPLEX, 0.75, (0, 0, 255), 2)
    draw2 = cv2.putText(draw2, target_x_display, (int(target_x / 2), int(target_y)), cv2.FONT_HERSHEY_SIMPLEX, 0.75,
                        (255, 0, 0), 2)
    draw2 = cv2.putText(draw2, target_y_display, (int(target_x), int(target_y / 2)), cv2.FONT_HERSHEY_SIMPLEX, 0.75,
                        (255, 0, 0), 2)



    cv2.imshow('ROI_image', imgroi)
    cv2.imshow('frame_with_rectangle', frame)
    cv2.imshow('Original_from_camera', img)
    cv2.imshow('target', draw2)
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break
cap.release()
cv2.destroyAllWindows()

.
.

运行结果图片展示

.
.
最终运行结果如下:

使用OpenCV中的分类器和颜色识别的苹果位置识别_第8张图片
.
.

我的代码,训练分类器的文件夹下载

.
.

已上传百度网盘

链接:https://pan.baidu.com/s/1S7wXacR8qolPSqACd3wosA
提取码:0000

.
.

本文组织架构

    • 环境配置和安装
    • 拍摄并处理样本
    • 生成正样本的描述文件pos.vec
    • 开始训练分类器
    • 单目测距
    • 完成分类器与颜色识别,轮廓提取算法的结合
    • 运行结果图片展示
    • 我的代码,训练分类器的文件夹下载


你可能感兴趣的:(python)