• OpenCV形状检测


    1.基于OpenCV的形状检测Python版本

    目录结构
    在这里插入图片描述

    1.1.定义我们的形状检测器类ShapeDetector

    开始定义我们的 ShapeDetector 类。我们将跳过这里的 init 构造函数,因为不需要初始化任何东西。

    # 导入必要的包
    import cv2
    
    
    class ShapeDetector:
        def __init__(self):
            pass
    
        def detect(self, c):
            # 初始化形状名称并近似轮廓
            shape = "unidentified"
            peri = cv2.arcLength(c, True)
            approx = cv2.approxPolyDP(c, 0.04 * peri, True)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    我们的检测方法,它只需要一个参数 c,即我们试图识别的形状的轮廓。为了进行形状检测,我们将使用轮廓逼近。
    顾名思义,轮廓近似是一种算法,用于减少曲线中点的数量,并减少点集——因此称为近似。
    轮廓近似是基于曲线可以由一系列短线段近似的假设。这导致生成的近似曲线由原始曲线定义的点的子集组成。
    轮廓近似实际上已经在 OpenCV 中通过 cv2.approxPolyDP 方法实现。
    为了进行轮廓逼近,我们首先计算轮廓的周长,然后构建实际的轮廓逼近。 cv2.approxPolyDP 的第二个参数的常用值通常在原始轮廓周长的 1%-5% 范围内。

    给定我们的近似轮廓,我们可以继续执行形状检测:

    	    # 如果形状是一个三角形,它将有3个顶点
    	    if len(approx) == 3:
    	        shape = "triangle"
    	    # 如果形状有4个顶点,它要么是正方形,要么是矩形
    	    elif len(approx) == 4:
    	        # 计算轮廓的包围框,并使用包围框计算高宽比
    	        (x, y, w, h) = cv2.boundingRect(approx)
    	        ar = w / float(h)
    	        # 正方形的长宽比大约等于1,否则,形状就是矩形
    	        shape = "square" if ar >= 0.95 and ar <= 1.05 else "rectangle"
    	    # 如果形状是一个五边形,它将有5个顶点
    	    elif len(approx) == 5:
    	        shape = "pentagon"
    	    # 否则,我们假设形状是一个圆
    	    else:
    	        shape = "circle"
    	    # 返回形状的名称
    	    return shape
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    重要的是要了解轮廓由顶点列表组成。我们可以检查此列表中的数目以确定对象的形状。例如,如果近似轮廓有三个顶点,
    那么它一定是一个三角形。如果一条轮廓有四个顶点,那么它一定是正方形或矩形。为了确定这一点,我们计算形状的长宽比,
    也就是轮廓边界框的宽度除以高度。如果长宽比是~1.0,那么我们正在检查一个正方形(因为所有的边都有大约相等的长度)。否则,形状就是矩形。如果一条等高线有五个顶点,我们可以将其标记为五边形。否则,我们可以假设我们正在检查的形状是一个圆。
    最后,将标识好的形状返回给调用方法。

    我们已经定义了一个utils模块。在这个模块中,我们有shapedetector .py,它将存储ShapeDetector类的实现。
    最后,我们有detect_shapes.py脚本,我们将使用它从磁盘加载图像,分析它的形状,然后通过ShapeDetector类执行形状检测和识别。

    1.2.基于OpenCV的形状检测器

    现在我们的 ShapeDetector 类已经定义好了,让我们创建 detect_shapes.py 脚本:

    # 导入必要的库
    from utils.shapedetector import ShapeDetector
    import argparse
    import imutils
    import cv2
    
    # 构造参数解析并解析参数
    ap = argparse.ArgumentParser()
    ap.add_argument("-i", "--image", default="inpaint.jpg", help="path to the input image")
    args = vars(ap.parse_args())
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    首先导入我们已经实现的ShapeDetector类,然后解析参数,下一步,我们开始预处理我们的图像

    # 加载图像并将其调整图像大小,以便更好地近似形状
    image = cv2.imread(args["image"])
    resized = imutils.resize(image, width=300)
    ratio = image.shape[0] / float(resized.shape[0])
    # 将调整后的图像转换为灰度,稍微模糊它,并阈值化
    gray = cv2.cvtColor(resized, cv2.COLOR_BGR2GRAY)
    blurred = cv2.GaussianBlur(gray, (5, 5), 0)
    thresh = cv2.threshold(blurred, 60, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]
    # 在阈值化图像中找到轮廓并初始化形状检测器
    cnts = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL,
                            cv2.CHAIN_APPROX_SIMPLE)
    cnts = imutils.grab_contours(cnts)
    sd = ShapeDetector()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    首先,我们从磁盘加载图像,并调整其大小。然后,我们跟踪旧高度与调整后的新高度的比率——我们将在本教程的后面部分找到这样做的确切原因。
    将调整大小后的图像转换为灰度,平滑它以减少高频噪声,最后阈值化它以显示图像中的形状。
    阈值之后,我们的图像应该是这样的:
    在这里插入图片描述

    请注意我们的图像是如何被二值化的——形状显示为黑色背景下的白色前景。
    最后,在二值图像中找到轮廓,基于OpenCV版本的cv2.findContours获取正确的元组值,并最终初始化我们的ShapeDetector。
    最后一步是识别每个轮廓:

    # 遍历所有轮廓
    for c in cnts:
        # 计算轮廓的中心,然后仅使用轮廓检测形状的名称
        M = cv2.moments(c)
        cX = int((M["m10"] / M["m00"]) * ratio)
        cY = int((M["m01"] / M["m00"]) * ratio)
        shape = sd.detect(c)
        # 将轮廓(x, y)坐标乘以调整比例,然后在图像上绘制轮廓和形状的名称
        c = c.astype("float")
        c *= ratio
        c = c.astype("int")
        cv2.drawContours(image, [c], -1, (0, 255, 0), 2)
        cv2.putText(image, shape, (cX, cY), cv2.FONT_HERSHEY_SIMPLEX,
                    0.5, (255, 255, 255), 2)
        # 显示输出图像
        cv2.imshow("Image", image)
        cv2.waitKey(0)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    我们开始在每个单独的轮廓上循环。对于每一个,我们计算轮廓的中心,然后执行形状检测和识别。
    由于我们正在处理从调整大小后的图像中提取的轮廓(而不是原始图像),我们需要将轮廓和中心(x, y)坐标乘以调整比率。
    这将为我们提供原始图像的轮廓和质心的正确(x, y)坐标。
    最后,我们在图像上绘制轮廓和识别的形状,然后显示我们的结果。
    在这里插入图片描述

    2.基于OpenCV的形状检测C++版本

    在本教程中,让我们看看如何使用 OpenCV 的轮廓来识别对象的形状和位置。
    使用OpenCV的轮廓,你可以得到每个白斑的顶点的点序列(白斑被认为是多边形)。例如,对于三角形你会得到3个点(顶点),
    对于四边形你会得到4个点。你可以通过多边形的顶点数来识别任何多边形。
    你甚至可以通过计算和比较顶点之间的距离来识别多边形的特征,如凸性、凹性、等边等。

    我们看看如何使用 OpenCV 来完成。您所需要的只是一个二进制图像,其中您的对象应该是白色的,背景应该是黑色的。
    现在我将使用OpenCV C++应用程序来识别上图中的三角形、四边形和七边形。我将沿着每个确定的多边形的周长画一条线,
    三角形颜色为蓝色,四边形颜色为绿色,七边形颜色为红色。

    2.1代码实现

    #include 
    using namespace std;
    using namespace cv;
    
    int main()
    {
    
        Mat img = imread("FindingContours.png");
    
        //show the original image
        namedWindow("Raw");
        imshow("Raw", img);
    
        //converting the original image into grayscale
        Mat imgGrayScale = Mat(img.size(), CV_8UC1);
        cvtColor(img, imgGrayScale, COLOR_BGR2GRAY);
    
        //thresholding the grayscale image to get better results
        threshold(imgGrayScale, imgGrayScale, 128, 255, THRESH_BINARY);
    
        //finding all contours in the image
        vector<vector<Point> > contours;
        vector<Vec4i> hierarchy;
        findContours(imgGrayScale, contours, hierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE);
    
    
        //iterating through each contour
        for (size_t i = 0; i < contours.size(); i++)
        {
            vector<Point> approx;
            //obtain a sequence of points of contour, pointed by the variable 'contour'
            approxPolyDP(contours[i], approx, arcLength(contours[i], true) * 0.02, true);
    
            //if there are 3  vertices  in the contour(It should be a triangle)
            if (approx.size() == 3)
            {
                //drawing lines around the triangle
                for (int i = 0; i < 3; i++) {
                    line(img, approx[i], approx[(i+1)%3], Scalar(255, 0, 0), 4);
                }
            }
            //if there are 4 vertices in the contour(It should be a quadrilateral)
            else if (approx.size() == 4)
            {
                //drawing lines around the quadrilateral
                for (int i = 0; i < 4; i++) {
                    line(img, approx[i], approx[(i + 1) % 4], Scalar(0, 255, 0), 4);
                }
            }
    
            //if there are 7  vertices  in the contour(It should be a heptagon)
            else if (approx.size() == 7)
            {
                //drawing lines around the heptagon
                for (int i = 0; i < 7; i++) {
                    line(img, approx[i], approx[(i + 1) % 7], Scalar(0, 0, 255), 4);
                } 
            }
        }
    
        //show the image in which identified shapes are marked   
        namedWindow("Tracked");
        imshow("Tracked", img);
    
        waitKey(0); //wait for a key press
    
        //cleaning up
        destroyAllWindows();
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71

    如您所见,三角形用蓝色标记, 四边形用绿色标记,七边形用红色标记。所以,现在很明显,这种方法能够识别形状。 首先将原始图像转换为灰度。
    这是因为这种方法只适用于单通道的灰度图像。为了获得更好的结果,我使用“threshold”函数对灰度图像进行阈值处理。
    您可以使用自己的方式对图像进行阈值处理。然后我找到阈值图像中的所有轮廓,并识别和跟踪所有三角形、四边形和七边形。

    2.2主要函数解析

    让我们讨论一下这个应用程序中的OpenCV 函数。
    double threshold(InputArray src, OutputArray dst, double thresh, double maxval, int type);

    • src:源图像(8位单通道)
    • dst:与src具有相同大小相同类型的目标图像
    • thresh:阈值
    • maxval:滿足條件的像素替换为这个值
    • type:阈值化方法,
      • THRESH_BINARY
        dst(x,y)=max, if src(x,y) > ThreshVal
        dst(x,y)=0, if src(x,y) < ThreshVal
      • THRESH_BINARY_INV
        dst(x,y)=0,如果 src(x,y) > ThreshVal
        dst(x,y)=max,如果 src(x,y) < ThreshVal
      • THRESH_TOZERO
        dst(x,y)=src(x,y), 如果 src(x,y) > ThreshVal
        dst(x,y)=0, 如果 src(x,y) < ThreshVal
      • THRESH_TOZERO_INV
        dst(x,y)=0,如果 src(x,y) > ThreshVal
        dst(x,y)=src(x,y),如果 src(x,y) < ThreshVal
      • THRESH_TRUNC
        dst(x,y)=threshVal,如果 src(x,y) > ThreshVal
        dst(x,y)=src(x,y), if src(x,y) < ThreshVal

    在上面的应用程序中,我使用了“ THRESH_BINARY”,因为我想在对象所在的位置分配 255(白色其他是0(黑色)。

    findContours( InputArray image, OutputArrayOfArrays contours, OutputArray hierarchy, int mode, int method, Point offset = Point());

    • image: 8位单通道图像。非零像素被视为1。零像素保持为0,因此图像被视为二值。你可以使用#compare #inRange #threshold,#adaptiveThreshold, #Canny,以及其他参数来创建灰度或彩色图像的二值图像。

    • contours: 发现的所有轮廓

    • hierarchy:轮廓之间的层次结构

    • int mode - 从图像中检索轮廓的模式,您必须选择以下之一
      RETR_LIST - 检索所有轮廓并将它们放入列表中
      RETR_EXTERNAL - 仅检索外轮廓
      RETR_CCOMP - 检索所有轮廓并将它们组织成两级层次结构:
      RETR_TREE - 检索所有轮廓并重建嵌套轮廓的完整层次结构

    • int method - 近似方法,您必须选择以下之一
      CHAIN_APPROX_NONE - 将链码中的所有点转换为点
      CHAIN_APPROX_SIMPLE - 压缩水平、垂直和对角线段,只留下它们的端点
      CHAIN_APPROX_TC89_L1,CV_CHAIN_APPROX_TC89_KCOS - 应用 Teh-Chin 链近似算法的一种风格。

    • Point offset:每个轮廓点应移动的偏移量。当我们在图像中设置 ROI(感兴趣区域)时,这很有用。通常我们将偏移量设置为 ‘Point(0,0)’

    void approxPolyDP( InputArray curve, OutputArray approxCurve, double epsilon, bool closed );

    • curve: 存储2D点的std::vector或Mat
    • approxCurve: 多边形近似的结果。类型应该与输入类型相匹配。
    • epsilon: 指定近似精度的参数。这是原始轮廓与其近似值之间的最大距离。
    • closed: 如果为真,逼近曲线是闭合的(它的第一个和最后一个顶点是连接的)。否则不闭合。

    2.3结果展示

    在这里插入图片描述

    参考目录

    https://pyimagesearch.com/2016/02/08/opencv-shape-detection/

  • 相关阅读:
    pytorch数学运算
    3天快速入门python机器学习(黑马xxx)
    基数排序——【常见排序法(2/8)】
    EtherCAT总线控制伺服力矩控制功能块TorqueControl_FB(汇川H5U PLC)
    1688搜索店铺列表 API
    【Docker】 ubuntu18.04编译时内存不足需要使用临时交换分区解决“c++: internal compiler error“错误
    “加密上海·喜玛拉雅Web3.0数字艺术大展”落幕,AIGC和数字艺术衍生品是最大赢家?...
    Elastic:总结收集日志的几种方法
    Elasticsearch 中 term和 query 哪个更精确,有什么区别
    【RF分类】基于matlab随机森林算法数据分类【含Matlab源码 2048期】
  • 原文地址:https://blog.csdn.net/weixin_43229348/article/details/126071797