ui自学网站,商城小程序定制,音乐如何到wordpress,网站开发工具概述与比较ubuntu22.04laptop OpenCV Get Started: 013_contour_detection 1. 源由2. 应用Demo2.1 C应用Demo2.2 Python应用Demo 3. contour_approx应用3.1 读取图像并将其转换为灰度格式3.2 应用二进制阈值过滤算法3.3 查找对象轮廓3.4 绘制对象轮廓3.5 效果3.6 CHAIN_APPROX_SIMPLE v.s… ubuntu22.04laptop OpenCV Get Started: 013_contour_detection 1. 源由2. 应用Demo2.1 C应用Demo2.2 Python应用Demo 3. contour_approx应用3.1 读取图像并将其转换为灰度格式3.2 应用二进制阈值过滤算法3.3 查找对象轮廓3.4 绘制对象轮廓3.5 效果3.6 CHAIN_APPROX_SIMPLE v.s.CHAIN_APPROX_NONE 4. channel_experiments应用4.1 读取图像并将其转换为单通道4.2 查找对象轮廓4.3 绘制对象轮廓4.4 效果 5. contour_extraction应用5.1 RETR_LIST5.2 RETR_EXTERNAL5.3 RETR_CCOMP5.4 RETR_TREE5.5 运行时间比较 6. 总结7. 参考资料8. 补充 1. 源由
使用轮廓检测检测物体的边界通常是许多有趣应用的第一步如图像前景提取、简单的图像分割、检测和识别。
罗列一些使用轮廓进行运动检测或分割应用
运动检测在监控视频中运动检测技术有许多应用包括室内和室外安全环境、交通控制、体育活动中的行为检测、无人看管物体的检测甚至视频压缩。在下图中可以看到检测视频流中的人员移动在监控应用程序中是如何有用的。请注意未检测到静止在图像左侧的那组人。只有运动中的人才会被捕捉到。请参考《Moving Object Detection and Segmentation using Frame differencing and Summing Technique》详细研究这种方法。 无人值守物体检测公共场所的无人值守物体通常被视为可疑物体。一个有效且安全的解决方案详见《Unattended Object Detection through Contour Formation using Background Subtraction》。 背景/前景分割要用另一个图像替换图像的背景需要执行图像前景提取类似于图像分割。使用轮廓是可以用于执行分割的一种方法。有关更多详细信息请参阅《Background Removal with Python Using OpenCV to Detect the Foreground》。 2. 应用Demo
013_contour_detection是OpenCV通过鼠标指针和轨迹条与用户交互的示例。
2.1 C应用Demo
C应用Demo工程结构
013_contour_detection/CPP$ tree . .
├── channel_experiments
│ ├── channel_experiments.cpp
│ └── CMakeLists.txt
├── contour_approximations
│ ├── CMakeLists.txt
│ └── contour_approx.cpp
└── contour_extraction├── CMakeLists.txt└── contour_extraction.cpp3 directories, 6 files确认OpenCV安装路径
$ find /home/daniel/ -name OpenCVConfig.cmake
/home/daniel/OpenCV/installation/opencv-4.9.0/lib/cmake/opencv4/
/home/daniel/OpenCV/opencv/build/OpenCVConfig.cmake
/home/daniel/OpenCV/opencv/build/unix-install/OpenCVConfig.cmake$ export OpenCV_DIR/home/daniel/OpenCV/installation/opencv-4.9.0/lib/cmake/opencv4/C应用Demo工程编译执行
$ mkdir build
$ cd build
$ cmake ..
$ cmake --build . --config Release
$ cd ..
$ ./build/channel_experiments$ mkdir build
$ cd build
$ cmake ..
$ cmake --build . --config Release
$ cd ..
$ ./build/contour_approximations$ mkdir build
$ cd build
$ cmake ..
$ cmake --build . --config Release
$ cd ..
$ ./build/contour_extraction2.2 Python应用Demo
Python应用Demo工程结构
013_contour_detection/Python$ tree .
.
├── channel_experiments
│ └── channel_experiments.py
├── contour_approximations
│ └── contour_approx.py
├── contour_extraction
│ └── contour_extraction.py
└── requirements.txt3 directories, 4 filesPython应用Demo工程执行
$ workoncv-4.9.0
$ python channel_experiments.py
$ python contour_approx.py
$ python contour_extraction.py3. contour_approx应用
在OpenCV中检测和绘制轮廓的步骤如下所示
读取图像并将其转换为灰度格式应用二进制阈值过滤算法查找对象轮廓绘制对象轮廓
查找轮廓可以采用两种方式
CHAIN_APPROX_SIMPLECHAIN_APPROX_NONE
3.1 读取图像并将其转换为灰度格式
读取图像并将图像转换为灰度格式。将图像转换为灰度非常重要因为它为下一步的图像做准备。
将图像转换为单通道灰度图像对于阈值处理很重要这反过来又是轮廓检测算法正常工作所必需的。
C: // read the imageMat image imread(../../input/image_1.jpg);// convert the image to grayscale formatMat img_gray;cvtColor(image, img_gray, COLOR_BGR2GRAY);Python:
# read the image
image cv2.imread(../../input/image_1.jpg)
# convert the image to grayscale format
img_gray cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)3.2 应用二进制阈值过滤算法
在寻找轮廓时首先总是对灰度图像应用二进制阈值或Canny边缘检测。在这里我们将应用二进制阈值。
这将图像转换为黑白图像突出显示感兴趣的对象从而简化轮廓检测算法。阈值化使图像中对象的边界完全变白所有像素都具有相同的强度。该算法现在可以从这些白色像素中检测物体的边界。
C: // apply binary thresholdingMat thresh;threshold(img_gray, thresh, 150, 255, THRESH_BINARY);Python:
# apply binary thresholding
ret, thresh cv2.threshold(img_gray, 150, 255, cv2.THRESH_BINARY)3.3 查找对象轮廓
C: // detect the contours on the binary image using cv2.CHAIN_APPROX_NONEvectorvectorPoint contours;vectorVec4i hierarchy;findContours(thresh, contours, hierarchy, RETR_TREE, CHAIN_APPROX_NONE);########## 两种轮廓检测方法所以有两段对应的代码// Now lets try with CHAIN_APPROX_SIMPLE// detect the contours on the binary image using cv2.CHAIN_APPROX_NONEvectorvectorPoint contours1;vectorVec4i hierarchy1;findContours(thresh, contours1, hierarchy1, RETR_TREE, CHAIN_APPROX_SIMPLE);Python:
# detect the contours on the binary image using cv2.CHAIN_APPROX_NONE
contours, hierarchy cv2.findContours(imagethresh, modecv2.RETR_TREE, methodcv2.CHAIN_APPROX_NONE)########## 两种轮廓检测方法所以有两段对应的代码
# detect the contours on the binary image using cv2.ChAIN_APPROX_SIMPLE
contours1, hierarchy1 cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)3.4 绘制对象轮廓
C: // draw contours on the original imageMat image_copy image.clone();drawContours(image_copy, contours, -1, Scalar(0, 255, 0), 2);imshow(None approximation, image_copy);########## 两种轮廓检测方法所以有两段对应的代码// draw contours on the original imageMat image_copy1 image.clone();drawContours(image_copy1, contours1, -1, Scalar(0, 255, 0), 2);imshow(Simple approximation, image_copy1);Python:
# draw contours on the original image
image_copy image.copy()
cv2.drawContours(imageimage_copy, contourscontours, contourIdx-1, color(0, 255, 0), thickness2, lineTypecv2.LINE_AA)
# see the results
cv2.imshow(None approximation, image_copy)########## 两种轮廓检测方法所以有两段对应的代码
# draw contours on the original image for CHAIN_APPROX_SIMPLE
image_copy1 image.copy()
cv2.drawContours(image_copy1, contours1, -1, (0, 255, 0), 2, cv2.LINE_AA)
# see the results
cv2.imshow(Simple approximation, image_copy1)3.5 效果
该算法生成的轮廓很好地识别了每个对象的边界。然而如果你仔细观察手机你会发现它包含不止一个轮廓。已经为与相机镜头和光线相关的圆形区域确定了单独的轮廓。沿着手机边缘的部分也有“次要”轮廓。
3.6 CHAIN_APPROX_SIMPLE v.s.CHAIN_APPROX_NONE
轮廓算法的准确性和质量在很大程度上取决于所提供的二进制图像的质量。某些场景需要高质量的轮廓因此在创建二进制图像时使用不同的阈值进行实验看看这是否会改善生成的轮廓。
另外两种方法执行完后将轮廓进行描点可以发现CHAIN_APPROX_NONE点数多于CHAIN_APPROX_SIMPLE
CHAIN_APPROX_SIMPLE算法沿轮廓压缩水平、垂直和对角线段只保留它们的端点。这意味着沿着直线路径的任何点都将被忽略将只剩下终点。
例如考虑一个沿着矩形的轮廓。除四个角点外所有轮廓点都将被忽略。此方法比CHAIN_APPROX_NONE更快因为该算法不存储所有点使用更少的内存因此执行所需时间更少。 4. channel_experiments应用
在OpenCV中检测和绘制轮廓的步骤如下所示
读取图像并将其转换为单通道红色、绿色或蓝色查找对象轮廓绘制对象轮廓
4.1 读取图像并将其转换为单通道
C: // read the imageMat image imread(../../input/image_1.jpg);// B, G, R channel splittingMat channels[3];split(image, channels);Python:
# read the image
image cv2.imread(../../input/image_1.jpg)# B, G, R channel splitting
blue, green, red cv2.split(image)4.2 查找对象轮廓
C: // detect contours using blue channel and without thresholdingvectorvectorPoint contours1;vectorVec4i hierarchy1;findContours(channels[0], contours1, hierarchy1, RETR_TREE, CHAIN_APPROX_NONE);// detect contours using green channel and without thresholdingvectorvectorPoint contours2;vectorVec4i hierarchy2;findContours(channels[1], contours2, hierarchy2, RETR_TREE, CHAIN_APPROX_NONE);// detect contours using red channel and without thresholdingvectorvectorPoint contours3;vectorVec4i hierarchy3;findContours(channels[2], contours3, hierarchy3, RETR_TREE, CHAIN_APPROX_NONE);Python:
# detect contours using blue channel and without thresholding
contours1, hierarchy1 cv2.findContours(imageblue, modecv2.RETR_TREE, methodcv2.CHAIN_APPROX_NONE)# detect contours using green channel and without thresholding
contours2, hierarchy2 cv2.findContours(imagegreen, modecv2.RETR_TREE, methodcv2.CHAIN_APPROX_NONE)# detect contours using red channel and without thresholding
contours3, hierarchy3 cv2.findContours(imagered, modecv2.RETR_TREE, methodcv2.CHAIN_APPROX_NONE)4.3 绘制对象轮廓
C: // draw contours on the original imageMat image_contour_blue image.clone();drawContours(image_contour_blue, contours1, -1, Scalar(0, 255, 0), 2);imshow(Contour detection using blue channels only, image_contour_blue);// draw contours on the original imageMat image_contour_green image.clone();drawContours(image_contour_green, contours2, -1, Scalar(0, 255, 0), 2);imshow(Contour detection using green channels only, image_contour_green);// draw contours on the original imageMat image_contour_red image.clone();drawContours(image_contour_red, contours3, -1, Scalar(0, 255, 0), 2);imshow(Contour detection using red channels only, image_contour_red);Python:
# draw contours on the original image
image_contour_blue image.copy()
cv2.drawContours(imageimage_contour_blue, contourscontours1, contourIdx-1, color(0, 255, 0), thickness2, lineTypecv2.LINE_AA)
# see the results
cv2.imshow(Contour detection using blue channels only, image_contour_blue)# draw contours on the original image
image_contour_green image.copy()
cv2.drawContours(imageimage_contour_green, contourscontours2, contourIdx-1, color(0, 255, 0), thickness2, lineTypecv2.LINE_AA)
# see the results
cv2.imshow(Contour detection using green channels only, image_contour_green)# draw contours on the original image
image_contour_red image.copy()
cv2.drawContours(imageimage_contour_red, contourscontours3, contourIdx-1, color(0, 255, 0), thickness2, lineTypecv2.LINE_AA)
# see the results
cv2.imshow(Contour detection using red channels only, image_contour_red)4.4 效果
在下面的图像中我们可以看到轮廓检测算法不能正确地找到轮廓。这是因为它不能正确地检测对象的边界而且像素之间的强度差也没有很好地定义。这就是我们更喜欢使用灰度和二进制阈值图像来检测轮廓的原因。
另外CHAIN_APPROX_SIMPLE和CHAIN_APPROX_NONE并没有差异同样无法找到轮廓。 5. contour_extraction应用
1. 轮廓之间的层次结构表示为父子关系
1、2、3和4是独立的对象3a是3个孩子1、2和4都是父形状没有任何关联的子形状因此它们的编号可以是任意的
2. 轮廓层次表示为一个数组该数组又包含四个值的数组。它表示为 [Next, Previous, First_Child, Parent] Next轮廓表示图像中处于相同层级的下一个轮廓。因此 对于轮廓1与其处于相同层级的下一个轮廓是2。在这里Next 将是2。 . 相应地轮廓3没有与其自身处于相同层级的轮廓。因此它的 Next 值将是 -1。 Previous轮廓表示相同层级上的上一个轮廓。这意味着轮廓1的上一个值始终为 -1。 First_Child表示我们当前考虑的轮廓的第一个子轮廓。 轮廓1和2根本没有子轮廓。因此它们的 First_Child 的索引值将为 -1。 但是轮廓3有一个子轮廓。因此对于轮廓3First_Child 的位置值将是3a的索引位置。 Parent 表示当前轮廓的父轮廓的索引位置。 轮廓1和2显而易见地没有任何父轮廓。 对于轮廓3a其父轮廓将是轮廓3。 对于轮廓4父轮廓是轮廓3a。 目前有四种不同的轮廓检索技术
RETR_LISTRETR_EXTERNALRETR_CCOMPRETR_TREE
5.1 RETR_LIST
RETR_LIST 轮廓检索方法不会在提取的轮廓之间创建任何父子关系。因此对于检测到的所有轮廓区域First_Child 和 Parent 索引位置值始终为 -1。
LIST: [[[ 1 -1 -1 -1]
[ 2 0 -1 -1]
[ 3 1 -1 -1]
[ 4 2 -1 -1]
[-1 3 -1 -1]]]C: findContours(thresh2, contours3, hierarchy3, RETR_LIST, CHAIN_APPROX_NONE);Python:
contours3, hierarchy3 cv2.findContours(thresh2, cv2.RETR_LIST, cv2.CHAIN_APPROX_NONE)5.2 RETR_EXTERNAL
RETR_EXTERNAL 轮廓检索方法非常有趣。它只检测父轮廓并忽略任何子轮廓。因此所有内部轮廓如3a和4将不会在其上绘制任何点。
EXTERNAL: [[[ 1 -1 -1 -1]
[ 2 0 -1 -1]
[-1 1 -1 -1]]]上述输出图像仅显示在轮廓1、2和3上绘制的点。轮廓3a和4被省略因为它们是子轮廓。
C: findContours(thresh2, contours4, hierarchy4, RETR_EXTERNAL, CHAIN_APPROX_NONE);Python:
contours4, hierarchy4 cv2.findContours(thresh2, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)5.3 RETR_CCOMP
与 RETR_EXTERNAL 不同RETR_CCOMP 检索图像中的所有轮廓。除此之外它还对图像中所有形状或对象应用了两级层次结构。
CCOMP: [[[ 1 -1 -1 -1]
[ 3 0 2 -1]
[-1 -1 -1 1]
[ 4 1 -1 -1]
[-1 3 -1 -1]]]上面的图像显示了层次级别为HL-1或HL-2分别表示级别1和级别2。现在让我们来看一下代码和输出的层次结构数组。
C: findContours(thresh2, contours5, hierarchy5, RETR_CCOMP, CHAIN_APPROX_NONE);Python:
contours5, hierarchy5 cv2.findContours(thresh2, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_NONE)5.4 RETR_TREE
就像 RETR_CCOMP 一样RETR_TREE 也检索图像中的所有轮廓。它还创建了一个完整的层次结构其级别不限于1或2。每个轮廓都可以有自己的层次结构与其所在级别和相应的父子关系一致。
TREE: [[[ 3 -1 1 -1]
[-1 -1 2 0]
[-1 -1 -1 1]
[ 4 0 -1 -1]
[-1 3 -1 -1]]]从上图可以明确看出
轮廓1、2和3位于同一级别即级别0。轮廓3a位于层次级别1因为它是轮廓3的子轮廓。轮廓4是一个新的轮廓区域所以其层次级别是2。
C: findContours(thresh2, contours6, hierarchy6, RETR_TREE, CHAIN_APPROX_NONE);Python:
contours6, hierarchy6 cv2.findContours(thresh2, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)5.5 运行时间比较
注不同的计算机时间可能有所差异这里仅仅做一个相对比较。
RETR_LIST 和 RETR_EXTERNAL 执行时间最短因为 RETR_LIST 不定义任何层次结构而 RETR_EXTERNAL 仅检索父轮廓。RETR_CCOMP 执行时间次之。它检索所有轮廓并定义了一个两级层次结构。RETR_TREE 执行时间最长因为它检索所有轮廓并为每个父子关系定义独立的层次级别。
6. 总结
本文通过findContours和drawContours来实现在OpenCV下寻找轮廓在图像上绘制轮廓边界凸显物体的方法。同时也讨论了关于轮廓之间的父子关系为后续物体识别奠定一定的基础。
findContours(image, contours, hierarchy, mode, method) image: The binary input image obtained in the previous step.mode: This is the contour-retrieval mode.method: This defines the contour-approximation method. drawContours(image, contours, contourIdx, color, thickness, lineType) image: This is the input RGB image on which you want to draw the contour.contours: Indicates the contours obtained from the findContours() function.contourIdx: The pixel coordinates of the contour points are listed in the obtained contours. Using this argument, you can specify the index position from this list, indicating exactly which contour point you want to draw. Providing a negative value will draw all the contour points.color: This indicates the color of the contour points you want to draw. We are drawing the points in green.thickness: This is the thickness of contour points. 7. 参考资料
【1】ubuntu22.04laptop OpenCV Get Started 【2】ubuntu22.04laptop OpenCV安装 【3】ubuntu22.04laptop OpenCV定制化安装
8. 补充
学习是一种过程对于前面章节学习讨论过的就不在文中重复了。
有兴趣了解更多的朋友请从《ubuntu22.04laptop OpenCV Get Started》开始一个章节一个章节的了解循序渐进。