亲宝软件园·资讯

展开

OpenCV形状检测

求则得之,舍则失之 人气:0

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)

我们的检测方法,它只需要一个参数 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.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())

首先导入我们已经实现的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()

首先,我们从磁盘加载图像,并调整其大小。然后,我们跟踪旧高度与调整后的新高度的比率——我们将在本教程的后面部分找到这样做的确切原因。

将调整大小后的图像转换为灰度,平滑它以减少高频噪声,最后阈值化它以显示图像中的形状。

阈值之后,我们的图像应该是这样的:

请注意我们的图像是如何被二值化的——形状显示为黑色背景下的白色前景。

最后,在二值图像中找到轮廓,基于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)

我们开始在每个单独的轮廓上循环。对于每一个,我们计算轮廓的中心,然后执行形状检测和识别。

由于我们正在处理从调整大小后的图像中提取的轮廓(而不是原始图像),我们需要将轮廓和中心(x, y)坐标乘以调整比率。

这将为我们提供原始图像的轮廓和质心的正确(x, y)坐标。

最后,我们在图像上绘制轮廓和识别的形状,然后显示我们的结果。

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

在本教程中,让我们看看如何使用 OpenCV 的轮廓来识别对象的形状和位置。

使用OpenCV的轮廓,你可以得到每个白斑的顶点的点序列(白斑被认为是多边形)。例如,对于三角形你会得到3个点(顶点),

对于四边形你会得到4个点。你可以通过多边形的顶点数来识别任何多边形。

你甚至可以通过计算和比较顶点之间的距离来识别多边形的特征,如凸性、凹性、等边等。

我们看看如何使用 OpenCV 来完成。您所需要的只是一个二进制图像,其中您的对象应该是白色的,背景应该是黑色的。

现在我将使用OpenCV C++应用程序来识别上图中的三角形、四边形和七边形。我将沿着每个确定的多边形的周长画一条线,

三角形颜色为蓝色,四边形颜色为绿色,七边形颜色为红色。

2.1代码实现

#include <opencv2/opencv.hpp>
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;
}

如您所见,三角形用蓝色标记, 四边形用绿色标记,七边形用红色标记。所以,现在很明显,这种方法能够识别形状。 首先将原始图像转换为灰度。

这是因为这种方法只适用于单通道的灰度图像。为了获得更好的结果,我使用“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

THRESH_BINARY_INV

THRESH_TOZERO

THRESH_TOZERO_INV

THRESH_TRUNC

在上面的应用程序中,我使用了“ 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 - 从图像中检索轮廓的模式,您必须选择以下之一

int method - 近似方法,您必须选择以下之一

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

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

2.3结果展示

加载全部内容

相关教程
猜你喜欢
用户评论