Advertisement

Opencv-Python Contour Shape Detection 轮廓形状检测

阅读量:

基本思路

第一步:对图像进行灰度化处理;然后运用模糊化方法生成边缘检测图;随后基于边缘检测图提取轮廓参数,并通过计算区域面积和边界数量来判断图形的形状特征。好的,请继续。

Opencv版本

该软件库是一个辅助工具包,建议全部安装以确保兼容性,并且与主程序采用一致版本以避免潜在问题。

在这里插入图片描述
在这里插入图片描述

函数介绍

cv2.findContours()

注意事项
  • 解析为二值图中,默认以黑色作为背景区域。
  • 该函数会对输入图像进行修改操作,在处理前应确保已复制一份原始图像副本。
  • 返回的参数数量会因cv2的不同版本而有所不同,默认情况下通常返回两个或三个参数。如果出现错误提示,则可以通过增减相关参数来应对。

extract contours from image using specified mode and method. The function accepts several parameters including the input image, contour extraction mode, extraction method, optional list of contours to store in hierarchy structure, and an optional offset parameter for further customization.

mode

参数名称 功能
cv2.RETR_EXTERNAL 只检测外轮廓
cv2.RETR_LIST 检测的轮廓不建立等级关系,都是同级
cv2.RETR_CCOMP 建立两个等级的轮廓,上面一层为外边界,里面一层为内孔的边界信息
cv2.RETR_TREE 建立一个等级树结构的轮廓

method

参数名称 功能
cv2.CHAIN_APPROX_NONE 存储所有边界点
cv2.CHAIN_APPROX_SIMPLE 压缩垂直、水平、对角方向,只保留端点
cv2.CHAIN_APPROX_TX89_L1 使用teh-Chini近似算法
cv2.CHAIN_APPROX_TC89_KCOS 使用teh-Chini近似算法

contours

图像轮廓点。列表格式,每一个元素为一个3维数组

hierarchy

层次关系采用三维数组的形式存在。其形状被定义为(1,n,4)。这里n代表了多少层结构?每个层级由四个数字构成。第一个数字指出同一层级中的下一个子层级开始的位置编号;第二个数字指出同一层级中上一个子层级结束的位置编号;第三个数字指示当前层级下一层级的具体节点数目;第四个数字则表明当前节点所处的高度层信息数量。

offset

轮 廓 点 的 偏 移 量 是 指 格 式 区 别 的 数 组 表 达 式 。 其 中 如 ( -1 , +1)表 示 轮 廓 点 沿 X 轴 负 方 向 移 动 单 像 素 并 沿 Y 轴 正 方 向 移 动 单 像 素 。 这 类 参 数 在 大 多 数 场 景 下 可 避 免 设置 , 因 此 这 类 参数 默认 不 配置 或 设置 成 不 使用 的 状态 。

返回值

(任意名称),contours,hierarchy 当版本发生变化时, 第一个参数将对应于新的版本进行调整.

cv2.drawContours()

OpenCV中的drawContours函数调用可以表示为:绘制轮廓图的过程涉及多个参数配置以实现特定效果的方法调用。例如,
cv2.drawContours(源图像, 轮廓列表, 指定索引编号, 颜色设置=..., 厚度参数=..., 线型选择=..., 指定层次结构=..., 最大深度级别=..., 偏移量设置=...)

image

注意要是三通道函数,可以调用原图像的复制图像

contourIdx

指定绘制轮廓list中的哪条轮廓,如果是-1,则绘制其中的所有轮廓

其余功能相对简单,在代码中也配有简明扼要的注释;如若对这些内容感兴趣,则可以通过网络资源进一步了解

最终代码

复制代码
    import cv2
    import numpy as np
    
    def stackImages(scale,imgArray):  # 堆栈操作
    rows = len(imgArray)
    cols = len(imgArray[0])
    rowsAvailable = isinstance(imgArray[0], list)
    width = imgArray[0][0].shape[1]
    height = imgArray[0][0].shape[0]
    if rowsAvailable:
        for x in range(0, rows):
            for y in range(0, cols):
                if imgArray[x][y].shape[:2] == imgArray[0][0].shape [:2]:
                    imgArray[x][y] = cv2.resize(imgArray[x][y], (0, 0), None, scale, scale)
                else:
                    imgArray[x][y] = cv2.resize(imgArray[x][y], (imgArray[0][0].shape[1], imgArray[0][0].shape[0]), None, scale, scale)
                if len(imgArray[x][y].shape) == 2 : imgArray[x][y]= cv2.cvtColor( imgArray[x][y], cv2.COLOR_GRAY2BGR)
        imageBlank = np.zeros((height, width, 3), np.uint8)
        hor = [imageBlank]*rows
        hor_con = [imageBlank]*rows
        for x in range(0, rows):
            hor[x] = np.hstack(imgArray[x])
        ver = np.vstack(hor)
    else:
        for x in range(0, rows):
            if imgArray[x].shape[:2] == imgArray[0].shape[:2]:
                imgArray[x] = cv2.resize(imgArray[x], (0, 0), None, scale, scale)
            else:
                imgArray[x] = cv2.resize(imgArray[x], (imgArray[0].shape[1], imgArray[0].shape[0]), None,scale, scale)
            if len(imgArray[x].shape) == 2: imgArray[x] = cv2.cvtColor(imgArray[x], cv2.COLOR_GRAY2BGR)
        hor= np.hstack(imgArray)
        ver = hor
    return ver
    
    def getContours(img):  #读取的图像要先转成灰度的
    contours, hierarchy = cv2.findContours(img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)  # 参数二为轮廓的检索模式(四种),cv2.RETR_EXTERNAL表示只检测外轮廓
    # 第三个参数method为轮廓的近似办法,cv2.CHAIN_APPROX_NONE存储所有的轮廓点,相邻的两个点的像素位置差不超过1
    for cnt in contours:  # 遍历每一个轮廓
        area = cv2.contourArea(cnt)  # 计算面积
        # print(area)
        if area > 500:  # 消除噪点,要求面积合格
            cv2.drawContours(imgContour, cnt, -1, (255, 0, 0), 3)  # 到此处为画边框
            peri = cv2.arcLength(cnt, True)  # permimeter周长,true意味着封闭的
            #print(peri)
            approx = cv2.approxPolyDP(cnt, 0.02*peri, True)  # 拐点 做一个近似0.02该值越小,拐弯处近似后越不明显
            # print(len(approx))
            objCor = len(approx)  # 多边形拟合后的点数,4方形,3三角,8圆
            x, y, w, h = cv2.boundingRect(approx)  # 边界框
    
            if objCor == 3:
                objectType ="Tri"
            elif objCor == 4:
                aspRatio = w/float(h)
                if aspRatio >0.98 and aspRatio <1.03: objectType= "Square" # 确定正方形
                else:objectType="Rectangle"
            elif objCor > 4: objectType= "Circles"
            else:objectType="None"
    
    
    
            cv2.rectangle(imgContour,(x,y),(x+w,y+h),(0,255,0),2)  # 画图形的矩形轮廓
            cv2.putText(imgContour, objectType,
                        (x+(w//2)-10, y+(h//2)-10), cv2.FONT_HERSHEY_COMPLEX, 0.7,  # 宽中心点,高中心点-10偏差,0.7 比例
                        (0, 0, 0), 2)
    
    # 主函数
    path = 'shapes.png'
    img = cv2.imread(path)
    imgContour = img.copy()  # 因为要修改参数,故需要复制原图
    
    imgGray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    imgBlur = cv2.GaussianBlur(imgGray, (7, 7), 1)  # 模糊为了降噪
    imgCanny = cv2.Canny(imgBlur, 50, 50)
    getContours(imgCanny)
    
    imgBlank = np.zeros_like(img)  # 制作一个黑色空图像ones为白色
    imgStack = stackImages(0.5, ([img, imgGray, imgBlur],
                             [imgCanny, imgContour, imgBlank]))
    
    cv2.imshow("Stack", imgStack)
    
    cv2.waitKey(0)

效果图

在这里插入图片描述

有什么不懂的地方可以下方留言或私信

Want to make every ordinary day overflow joy!

全部评论 (0)

还没有任何评论哟~