基于opencv的模式识别——水果类别识别与计数

        非常感谢那些有趣而又认真无私地在为知识普及做出贡献的网络博主们。

        此处特别感谢bilibili博主啥都会一点的研究生、CSDN博主YouCans、Pysource 博主Serjo以及freeCodeCamp.org将我真正带进了基于Python与Opencv库的计算机视觉领域的启蒙大门。

一、项目概述

        基于Pycharm编程平台与opencv开源库实现水果种类的简单识别与计数,识别样本来自实验小组拍摄。识别样本包括香蕉、苹果、柠檬、猕猴桃、红提以及橘子,拍摄角度涉及三方向拍摄(俯视、正视、侧视)。

二、简单的知识分享

(一)RGB颜色空间

空间模型

        对图像处理而言,RGB是最为重要和常见的颜色模型,它建立在笛卡尔坐标系中,以红、绿、蓝三种基本色为基础,进行不同程度的叠加,产生丰富而广泛的颜色,俗称三基色模式。

        RGB颜色空间是用一个单位长度的立方体来表示颜色的,黑蓝绿青红紫黄白8种常见颜色分别位居立方体的8个顶点,通常将黑色置于三维直角坐标系的原点,红绿蓝分别置于3根坐标轴上,整个立方体放在第一卦限内。青色与红色、紫色与绿色、黄色与蓝色是互补色。RGB各参数的取值范围是:R(0-255)、G(9-255)、B(0-255)。参数值也称为三色系数或基色系数或颜色值,除以255后归一到0-1之间,但不是无穷多个而是有限多个值。由于红绿蓝分量全部组合起来可表示很多不同的颜色,它比人眼能分辨的颜色种类多得多。

表达方式

        RGB空间是最常用的彩色信息表达方式,使用红、绿、蓝三原色的亮度来定量表示颜色,是以RGB三色光互相叠加来实现混色的方式。三种颜色所占比例不同,得到的颜色就不同。任何一种颜色在RGB颜色空间种都可以用三维空间中的一个点来表示。

F=r[R]+g[G]+b[B]

        色度学规则:

        1、通过RGB这三种颜色能产生任何颜色,并且这三种颜色混合后产生的颜色是唯一的;

        2、如果两个颜色相等,这三个颜色分量再乘以或者除以相同的数,得到的颜色仍然相等;

        3、混合色的亮度等于每种颜色亮度的和。

(二)HSV颜色空间

        HSV(Hue,Saturation,Value)是根据颜色的直观特性由A.R.Smith在1978年创建的一种颜色空间,也称六角锥体模型。六边形边界表示色彩,水平轴表示颜色纯度,明亮度沿垂直轴测量。

        色调(H):用角度度量,取值范围为0-360°,从红色开始按逆时针方向计算,红色为0°,绿色为120°,蓝色为240°。它们的补色是:黄色为60°,青色为180°,紫色为300°。

        饱和度(S):饱和度S表示颜色接近光谱色的程度。一种颜色,可以看成是某种光谱色与白色混合的结果。其中光谱色所占的比例愈大,颜色接近光谱色的程度就愈高,颜色的饱和度也就愈高。饱和度高,颜色则深而艳。光谱色的白光成分为零,饱和度达到最高。通常取值范围为0%-100%,值越大,颜色越饱和。

        明亮度(V):明度表示颜色明亮的程度,对于光源色,明度值与发光体的光亮度有关。对于物体色,此值和物体的透射比和反射比有关。

(三)固定像素大小格式

        VGA =640×480

        HD = 1280×720

        FHD=1920×1080

        4K=3840×2160

(四)高斯模糊开闭运算的作用

        在提取目标的时候,我们通常首先对原图像做一个高斯模糊操作,这是起到了减少图像噪声以及增强图像在不同比例大小下的图像效果;那么开闭运算通常是在获取到二值图像的边界信息后进行,这是主要起到了封闭提取目标边界的作用

        对于目标物轮廓等几何信息的获取来说,通常如何检验与封闭几何边界是最为重要的操作。

三、识别流程

四、实验数据分析

(一)切割提取对比

图一 苹果、香蕉、葡萄二值原图

 图二 柠檬、橘子、猕猴桃二值原图

图三 基于原图的掩膜

(二)数据分析折线

图四 不同水果类内HSV值差异

图五 不同水果间Sat饱和度指标平均值差异

图六 二值腐蚀一次处理后不同水果间几何特征差异

 图七 目标水果RGB波段的均值与均方根

 

图八 多目标差异观察曲线

五、项目代码

import math
import cv2
import numpy as np

# 数据预处理函数
def preProcess(Img):
    # 调整图片长宽像素比调整显示大小
    # 列为600行为400
    OriResize = cv2.resize(Img, (600, 400))
    # 转HSV颜色空间利用饱和度去除白色背景
    HSV = cv2.cvtColor(OriResize, cv2.COLOR_BGR2HSV)
    # 通过实验发现所使用背景可利用s_min=35的饱和度去除
    lower = np.array([0, 35, 0])
    upper = np.array([179, 255, 255])
    mask = cv2.inRange(HSV, lower, upper)
    # 进行高斯模糊
    blur = cv2.GaussianBlur(mask, (7, 7), 1)
    # 利用canny算子提取边缘
    canny = cv2.Canny(blur, 50, 50)
    # 进行一次闭运算,去除杂点
    kenel = np.ones((3, 3))
    mask1 = cv2.dilate(canny, kenel, iterations=1)
    mask2 = cv2.erode(mask1, kenel, iterations=1)
    return OriResize, mask2, blur


# 读入群体识别目标图
IMGpath = "Resources/all5.JPG"
OriImg = cv2.imread(IMGpath)
# 数据预处理
Ryuanshi, bianyuan, Obinary = preProcess(OriImg)
# 为RGB提取做原始数据备份
RYcopy = np.copy(Ryuanshi)
# 获取多目标轮廓信息
contours, hierarchy = cv2.findContours(bianyuan, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
# 利用约束规则与自设近似模型评价识别
for cnt in contours:
    # 利用封闭面积去除小区域边界影响,获取精确目标边界
    area = cv2.contourArea(cnt)
    if area < 100:
        continue
    else:
        # 构建特征向量
        feature = []
        # 未识别出的目标标识 unclassic
        objectType = "unclassic"
        # 判别模型
        # 第一步:获取面积与周长的比值
        pri = cv2.arcLength(cnt, True)
        div = area/pri
        feature.append(div)
        # 第二步:获取R,G,B信息
        # 将边界的点集利用DP算法压缩且保证误差在0.02倍周长内,假定边界闭合,获取最小外接矩形的左上坐标与长宽
        approx = cv2.approxPolyDP(cnt, 0.02 * pri, True)
        x, y, k, h = cv2.boundingRect(approx)
        # 加入定位框比
        if k > h:
            feature.append(k/h)
        else:
            feature.append(h/k)
        # 查看坐标
        print(x, y)
        MultOJ = cv2.bitwise_and(RYcopy, RYcopy, mask=Obinary)
        Bg, Gg, Rg = cv2.split(RYcopy)
        B, G, R = cv2.split(MultOJ)
        # 取背景板值样本
        udB = 0
        udG = 0
        udR = 0
        for i in range(10):
            for j in range(10):
                udB += Bg[j, i]
                udG += Gg[j, i]
                udR += Rg[j, i]
        udB /= 100
        udG /= 100
        udR /= 100
        # 正式开始获取颜色特征
        fMB = 0
        fMG = 0
        fMR = 0
        Rcount = 0
        for i in range(x, x+k):
            for j in range(y, y+h):
                if udB-8<B[j, i]<udB+8 and udG-8<G[j, i]<udG+8 and udR-8<R[j, i]<udR+8:
                    continue
                else:
                    fMB += B[j, i]
                    fMG += G[j, i]
                    fMR += R[j, i]
                    Rcount += 1
        fMB /= Rcount
        fMG /= Rcount
        fMR /= Rcount
        feature.append(fMB)
        feature.append(fMG)
        feature.append(fMR)
        # 求解方差
        fcB = 0
        fcG = 0
        fcR = 0
        for i in range(x, x+k):
            for j in range(y, y+h):
                if udB-8<B[j, i]<udB+8 and udG-8<G[j, i]<udG+8 and udR-8<R[j, i]<udR+8:
                    continue
                else:
                    fcB += pow((B[j, i]-fMB), 2)
                    fcG += pow((G[j, i] - fMG), 2)
                    fcR += pow((R[j, i] - fMR), 2)
        fcB = math.sqrt(fcB/Rcount)
        fcG = math.sqrt(fcG/Rcount)
        fcR = math.sqrt(fcR/Rcount)
        feature.append(fcB)
        feature.append(fcG)
        feature.append(fcR)
        # 特征查看检验处
        print(feature)
        # 利用特征开始进行判别
        if feature[1] > 1.2:
            if abs(feature[2]-feature[3]) <4:
                objectType = "grape"
            else:
               objectType = "banana"
        else:
            if abs(feature[2]-feature[3]) <4:
                objectType = "apple"
            else:
                if feature[7]-feature[5]>8 and feature[7]-feature[6]>8:
                    objectType = "orange"
                else:
                    objectType = "kiwifruit"
        # 绘制最小外接矩形与其对应边界框
        cv2.drawContours(Ryuanshi, cnt, -1, (0, 0, 255), 1)
        cv2.rectangle(Ryuanshi, (x, y), (x + k, y + h), (0, 255, 0))
        # 将目标物的类别显示于目标物的最小外接矩形框的左上角附近
        cv2.putText(Ryuanshi, objectType, (x-5, y-5), cv2.FONT_HERSHEY_COMPLEX, 0.5, (255, 0, 0), 1)
# 显示识别结果
cv2.imshow("Ryuanshi1", Ryuanshi)
cv2.waitKey(0)

六、项目结果展示 

        目标特征向量组成:面积与周长比、R均值及均方根、G均值及均方根、B均值及均方根。

        特征差异分析

                柠檬和橘子的差异或许可以从边缘点到最小外接矩形中心的距离的平均值求方差或曲率近似求差异。(代码未实现)

        类内、类间参数比较

        (一)%苹果%

        B均值和G均值差异极小;

        (二)%猕猴桃%

        决策树排除法(显然此处不适用添加六种水果以外的水果)

        (三)%葡萄%

        B均值和G均值差异极小;

        定位框长宽比显著高值;

        (四)%橘子%

        R均方根与B、G均方根差异一致显著,差异高于8;

        (五)%柠檬%

         R均方根与B、G均方根差异一致显著,差异高于11;

        (六)%香蕉%

         定位框长宽比显著高值;

        R均方根与B、G均方根差异一致显著,差异高于6;

        模型构建决策规则

        源代码清晰可见。

 

七、遇到的问题与思考

(一)在目标拍摄时,受到光线与拍摄角度的影响,单体目标拍摄与群体目标拍摄之间背景光亮值差异与目标颜色亮度差异如何有效解决?

        若可将拍摄角度固定(即正俯视拍摄),排除人为走动光线影响,按理来说应该足够完成限差。类比辐射定标的思想,或许可以:

①通过取多点背景值分别在单体与多体图像进行均值计算后,做差对比,线性定标归化;

②通过某种拉伸模型或者直方图规定化,映射归化值到单项目表模板。

(二)为什么当模型转至HSV颜色空间时(单一背景白色),通常可以用S特征(即饱和度)较好分出目标?

        通过饱和度的定义来讲,饱和度可以看作光谱色与白色的融合程度,饱和度越大,说明越接近光谱色,饱和度越小越接近浅色,那么答案显而易见。

(三)opencv中提供的计算面积函数cv2.contourArea()计算的是整个轮廓或部分轮廓的面积,轮廓的方向影响面积的符号。而我在实验中发现当轮廓出现明显布局拧结和绕环的情况下,此类目标轮廓通常会比其他轮廓顺滑明晰的目标面积小得多(目视解译原图两者差异大小不大),这是为什么?

        目前仍未能理解这个问题,但我认为可能和算法微积分有关?但是通过一次膨胀操作以后,我认为是可以解决同一基准下的度量对比问题。

(四)决策评价模型的加权差异构建与信任值约束、特征分离可信度也是一个极为头疼的事情,对于几何特征总是想找到拓扑关系或者类似不变矩等,但是受限于数学分析能力。对于模型构建我采用了类比决策树与机器学习中的奖励机制的方法。

(五)单目标图像的颜色特征与多目标图像差异较大,其或许存在光的散射和反射之间的复杂关系,此处问题若可行的话我认为加大目标间距可能比较好(基于作者现有的水平)。

参考资料:

[1]【不要再看那些过时的OpenCV老教程了】2022巨献,OpenCV零基础小白最新版全套教程(人工智能机器视觉教程)_哔哩哔哩_bilibili

[2]21. 项目实战-2.5 图像识别水果分类项目01_哔哩哔哩_bilibili(机器学习构建神经网络)

[3]【2022最好的OpenCV教程推荐】】B站最细的Python opencv特征提取与检测实战教程_哔哩哔哩_bilibili

[4]https://www.youtube.com/watch?v=oXlwWbU8l2o(源自Youtube)

[5]Opencv函数手册(Hand Book)——查阅使用说明,深入函数内部是我认为最好的学习方式

[6]3h精通Opencv-Python_哔哩哔哩_bilibili

[7]https://www.youtube.com/channel/UC5hHNks012Ca2o_MPLRUuJw(源自Youtube)

[8]百度百科

[9]解决pycharm安装opencv没有函数提醒的问题_今天上上签的博客-CSDN博客_pycharm导入第三方库怎么没有函数提醒

[10]Opencv之cv2.floodFill算法详解_啧啧啧biubiu的博客-CSDN博客_cv2 floodfill

[11]【OpenCV 例程200篇】223. 特征提取之多边形拟合(cv.approxPolyDP)_YouCans的博客-CSDN博客_opencv多边形拟合

文章出处登录后可见!

已经登录?立即刷新

共计人评分,平均

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

(0)
乘风的头像乘风管理团队
上一篇 2023年5月18日
下一篇 2023年5月18日

相关推荐