当前位置:网站首页>OpenCV形状检测
OpenCV形状检测
2022-07-30 16:39:00 【求则得之,舍则失之】
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
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
在上面的应用程序中,我使用了“ 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/
边栏推荐
猜你喜欢
Leetcode 119. 杨辉三角 II
加密生活,Web3 项目合伙人的一天
How to intercept the first few digits of a string in php
Public Key Retrieval is not allowed报错解决方案
[AGC] Quality Service 2 - Performance Management Example
你是一流的输家,你因此成为一流的赢家
PHP message feedback management system source code
(1) Cloud computing technology learning - virtualized vSphere learning
Visual Studio编辑器 2019:scanf函数返回值被忽略(C4996)报错及解决办法
hcip--ospf综合实验
随机推荐
数据的存储
Leetcode 118. Yanghui Triangle
【C语言】指针和数组的深入理解(第二期)
DTSE Tech Talk丨第2期:1小时深度解读SaaS应用系统设计
登录模块调试-软件调试入门
【SOC FPGA】外设KEY点LED
PCIE入门
武汉星起航跨境电商有前景吗?亚马逊的未来趋势如何发展?
onenote使用
How to remove last character from string in php
游戏显示分辨率的逆向分析
How to remove first character from php string
Mysql进阶优化篇01——四万字详解数据库性能分析工具(深入、全面、详细,收藏备用)
C语言学习之旅 【函数(二)】
Moonbeam创始人解读多链新概念Connected Contract
为什么中年男人爱出轨?
字符串加千分位符与递归数组求和
基于STM32F407使用ADC采集电压实验
FME's scheme and operation process for reading and writing cass data
node.js中怎么连接redis?