基于python OpenCV多边形图像识别的实现

岁酱吖の 提交于 2020-10-09 00:12:57

首先说一下我的整体思路:

① 首先定义了一个识别器类型,封装了计算边长,识别形状和展示结果三个函数。

② 主函数先读入图片,然后将图片转化为灰度图片,然后高斯滤波平滑处理,然后将灰度图片转化为黑白两色图片。

③ 调用函数识别图片中所有的轮廓,然后列表形式返回图片,轮廓等信息,然后只取轮廓的所有点信息(每个点的信息为平面坐标)作为一个列表程序(第91,92,93行代码)

④ 用之前创建的识别器实例对每个轮廓中的点进行多边形拟合,得到顶点的坐标的列表中去(class中28~32行代码)

⑤ 输出识别结果

以下是代码部分:

import cv2
import math

THRESHOLE_VALUE=60
COEFFICIENT=0.02

class ShapeDetector:
    #初始化类
    def __init__(self):
        #字典类型对应每一种图形的计数器
        self.counter = {
   
   "unrecognized image": 0, "triangle": 0, "rhombus": 0, "rectangle": 0, "pentagon": 0,
                        "hexagon": 0, "circle": 0}
        #初始化图形类型为不可识别
        self.shape = "unrecognized image"

        #图形顶点集置空
        self.approx = []

        #初始化该图形的周长为0
        self.peri = 0

    # 计算欧式距离(主要作用通过计算边长区分菱形和长方形)
    def distance(self, x1, y1, x2, y2):
        return math.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2)

    def detect(self, c):
        #cv2.arcLength函数返回周长
        self.peri = cv2.arcLength(c, True)

        #cv2.approxPolyDP用多边形取拟合,返回的是顶点的列表
        self.approx = cv2.approxPolyDP(c, COEFFICIENT * self.peri, True)

        #3个顶点,三角形
        if len(self.approx) == 3:
            self.shape = "triangle"

        #同理,四个顶点,四边形
        elif len(self.approx) == 4:
            #计算相邻两边的长度,做差判在误差范围内是否相等
            dist1 = self.distance(self.approx[0][0][0], self.approx[0][0][1], self.approx[1][0][0],
                                  self.approx[1][0][1])
            dist2 = self.distance(self.approx[0][0][0], self.approx[0][0][1], self.approx[3][0][0],
                                  self.approx[3][0][1])
            result = math.fabs(dist1 - dist2)
            # print(result)

            #误差小于10,可近似认为相等,为菱形
            if result <= 10:
                self.shape = "rhombus"
            else:
                self.shape = "rectangle"

        #五边形
        elif len(self.approx) == 5:
            self.shape = "pentagon"

        #六边形
        elif len(self.approx) == 6:
            self.shape = "hexagon"

        #圆
        else:
            self.shape = "circle"

        #相应形状计数器加一
        self.counter[self.shape] += 1

        #返回形状
        return self.shape

    def Display(self):
        #展现结果
        for kind in self.counter.keys():
            print("The number of {} is {}".format(kind, self.counter[kind]))


def main():
    #读入图片
    testID = "test.png"
    image = cv2.imread(testID)

    #将图片转换为灰度图片
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    # 高斯滤波,图像平滑处理
    blurred = cv2.GaussianBlur(gray, (5, 5), 0)
    #根据阈值,将灰度图片转化为黑白两色图片
    thresh = cv2.threshold(blurred, THRESHOLE_VALUE, 255, cv2.THRESH_BINARY_INV)[1]

    #返回图片和图中轮廓信息,列表形式返回到cnts中
    cnts = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    #只需要取轮廓上点的信息
    cnts = cnts[1]

    #创建一个识别器实例
    sd = ShapeDetector()

    #分别对每个轮廓进行处理
    for c in cnts:
        #得到形状
        shape = sd.detect(c)
        #print(shape)

    #输出结果
    sd.Display()

if __name__=="__main__":
    main()

以下为本程序用到的函数表:
(第9行)init(self) (类初始化函数)

(第23行)distance(self, x1, y1, x2, y2): (计算(x1,y1),(x2,y2)两点之间的距离,点为像素坐标)

(第24行)math.sqrt() (数学开平方运算)

(第26行)detect(self, c): (判断轮廓的形状)

(第28行)cv2.arcLength()(计算周长函数。一参数c是轮廓的点集;二参数true代表闭合,false代表开放。)
链接:https://blog.csdn.net/u011854789/article/details/79836242

(第31行) cv2.approxPolyDP()(多边形拟合函数。
一参数c是轮廓的点集;

二参数代表图形边长允许的偏差范围,因为图片中的图形边长上是凹凸不平的,但是电脑是根据标准的直线来进行边的拟合,所以难免每个点处都有和标准直线偏差的距离,此参数便是设置最大偏差距离不能超过多少,不然就会用一个新的边拟合,此处设置的是边长的百分之二;

三参数为true指示拟合的多边闭合,false为开放。 返回值为拟合出来的所有图形顶点坐标,用列表表示)

链接:https://blog.csdn.net/brooknew/article/details/103512228

(有好几行) len()(计算可迭代对象的长度)

(第71行)Display(self)(输出结果)

(第74行) print()(通过标准输出流将缓冲区信息输出到控制台(str.format的用法自己查))

(第79行) cv2.imread()(读入图片,
一参数图片路径;
二参数加载形式,可缺省)
链接:https://blog.csdn.net/lccrun/article/details/95594268


(第83行) cv2.cvtColor(转换颜色空间函数,
一参数为原图片;
二参数为转换的方法,常见的转化方法见链接)
链接:https://blog.csdn.net/m0_37192554/article/details/81946430


(第85行) cv2.GaussianBlur()(高斯滤波函数,图像平滑处理。
一参数是源图片;
二参数是高斯矩阵的大小;
三参数表示标准差。此函数用用就行不用搞懂具体干啥,如果感兴趣可以看下面的链接)
链接:https://blog.csdn.net/weixin_44657197/article/details/102679434



(第87行) cv2.threshold()(图像阈值处理函数,
一参数为源图片;
二参数为阈值;
三参数为设置颜色的最大RGB值;
四参数为划分的方法。



此函数的目的是为了将图片转化为黑白二色图,根据常识,颜色深的是黑色,颜色浅的是白色对吧,那么如何定义颜色的深浅呢?先补充一下:
***RGB:***计算机中常用的表示颜色的方法。计算机上每一个像素点都是三个颜色不同比例得到的,由一个元组来表示(R,G,B),分别代表红色,绿色和蓝色。
主流的划分是每个位置划分为256个段位,用0~255的整数表示,比如(255,255,255)就是最白的白色,(0,0,0)就是最黑的黑色,(255,0,0)就是最亮的红色。                            

***灰度:***前面的cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)就是将RGB彩色图片转换为灰度图片。具体的转换公式不用管,但是灰度为50的图片RGB值为(50,50,50)这个知道就行了。          

第二参数阈值:对图片的每个像素点,根据RGB值计算其灰度,如果灰度大于这个阈值,那么就变成黑色(或者白色),小于等于就变成白色(或者黑色)。

第三参数最大RGB值:就是把灰度变成255,说白了就是白色 第四参数划分方法:见下图
在这里插入图片描述dst(x,y)是(x,y)处像素之后的灰度,src(x,y)是源图片在(x,y)坐标像素的灰度)

链接:https://blog.csdn.net/a19990412/article/details/81172426

(第90行) cv2.findContours()(寻找图片中的所有轮廓,
一参数为带轮廓的图片;
二参数为找到的轮廓的输出形式;
三参数为指定轮廓的近似方法,返回值的话只需要取列表中的第二个,即可获得所有轮廓的所有点坐标。详细见链接)


链接:https://blog.csdn.net/u014120499/article/details/99675967

备注:
1、在使用cv2.threshold()函数时,第四个参数划分方法,当背景为白色时要用cv2.THRESH_BINARY_INV但是背景为黑色是要去掉_INV,用cv2.THRESH_BINARY。其他背景颜色自己想一想吧。。。。。。
2、要是统计的个数出现了问题的话,这是精度的问题,比如我当前设置下,圆的边数是8,改变精度只需要调整全局变量COEFFICIENT的大小就行

祝学习愉快~

评价(涂红):* * * * *

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!