Canny一类的边缘检测算法可以根据像素之间的差异,检测出轮廓边界的像素,但它没有将轮廓作为一个整体。所以要将轮廓提起出来,就必须将这些边缘像素组装成轮廓。

OpenCV中有一个很强大的函数,它可以从二值图像中找到轮廓:findContours函数。

有时我们还需要把找到的轮廓画出来,那就要用到函数drawContours了。

findContours函数和那就要用到函数drawContours函数一般配套使用。

#include "opencv2/imgproc.hpp"
#include "opencv2/highgui.hpp"
#include <iostream>
using namespace cv;
using namespace std;
void main()
{Mat original = imread("test5.jpg");namedWindow("My original");imshow("My original", original);Mat gray = original;cvtColor(gray, gray, CV_RGB2GRAY);//灰度化int thresh_size = (100 / 4) * 2 + 1; //自适应二值化阈值adaptiveThreshold(gray, gray, 255, CV_ADAPTIVE_THRESH_GAUSSIAN_C, CV_THRESH_BINARY_INV, thresh_size, thresh_size / 3);//morphologyEx(gray, gray, MORPH_OPEN, Mat());//形态学开运算去噪点imshow("gray", gray);vector<vector<Point> > contours;findContours(gray, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE); //找轮廓vector<vector<Point>> contours1;for (int i = 0; i < contours.size(); ++i){contours1.push_back(contours[i]);}Mat hole(gray.size(), CV_8U, Scalar(0)); //遮罩图层drawContours(hole, contours1, -1, Scalar(255), CV_FILLED); //在遮罩图层上,用白色像素填充轮廓,得到MASKnamedWindow("My hole");imshow("My hole", hole);Mat crop(original.rows, original.cols, CV_8UC3);original.copyTo(crop, hole);//将原图像拷贝进遮罩图层namedWindow("My warpPerspective");imshow("My warpPerspective", crop);waitKey(0);
}

右下角的图就是提取出来的轮廓图,真的是非常精准。不过精准只是因为原图的形状比较简单,如果遇到复杂图片,那情况就不太乐观了。

使用多边形把轮廓包围

在实际应用中,常常会有将检测到的轮廓用多边形表示出来的需求。比如在一个全家福中,我想用一个矩形框将我自己的头像框出来,这样就需要这方面的知识了。

OpenCv这方面的函数总结如下:

  • 返回指定点集最外部矩形边界:boundingRect()
  • 寻找给定的点集可旋转的最小包围矩形:minAreaRect()
  • 寻找最小包围圆形:minEnclosingCircle()
  • 用椭圆拟合二维点集:fitEllipse()
  • 逼近多边形曲线:approxPolyDP()

下面给出这些函数用法的综合案例。

#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include <iostream>
#include <stdio.h>
#include <stdlib.h>using namespace cv;
using namespace std;Mat src; Mat src_gray;
int thresh = 100;
int max_thresh = 255;
RNG rng(12345);/// 函数声明
void thresh_callback(int, void*);/** @主函数 */
int main(int argc, char** argv)
{/// 载入原图像, 返回3通道图像src = imread("test5.jpg", 1);/// 转化成灰度图像并进行平滑cvtColor(src, src_gray, CV_BGR2GRAY);blur(src_gray, src_gray, Size(3, 3));/// 创建窗口char* source_window = "Source";namedWindow(source_window, CV_WINDOW_AUTOSIZE);imshow(source_window, src);createTrackbar(" Threshold:", "Source", &thresh, max_thresh, thresh_callback);thresh_callback(0, 0);waitKey(0);return(0);
}/** @thresh_callback 函数 */
void thresh_callback(int, void*)
{Mat threshold_output;vector<vector<Point> > contours;vector<Vec4i> hierarchy;/// 使用Threshold检测边缘threshold(src_gray, threshold_output, thresh, 255, THRESH_BINARY);/// 找到轮廓findContours(threshold_output, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, Point(0, 0));/// 多边形逼近轮廓 + 获取矩形和圆形边界框vector<vector<Point> > contours_poly(contours.size());vector<Rect> boundRect(contours.size());vector<Point2f>center(contours.size());vector<float>radius(contours.size());for (int i = 0; i < contours.size(); i++){approxPolyDP(Mat(contours[i]), contours_poly[i], 3, true);boundRect[i] = boundingRect(Mat(contours_poly[i]));minEnclosingCircle(contours_poly[i], center[i], radius[i]);}/// 画多边形轮廓 + 包围的矩形框 + 圆形框Mat drawing = Mat::zeros(threshold_output.size(), CV_8UC3);for (int i = 0; i< contours.size(); i++){Scalar color = Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));drawContours(drawing, contours_poly, i, color, 1, 8, vector<Vec4i>(), 0, Point());rectangle(drawing, boundRect[i].tl(), boundRect[i].br(), color, 2, 8, 0);circle(drawing, center[i], (int)radius[i], color, 2, 8, 0);}/// 显示在一个窗口namedWindow("Contours", CV_WINDOW_AUTOSIZE);imshow("Contours", drawing);
}

下面两张不同阈值的效果图把检测到的轮廓分别用多边形、圆形、矩形框出来了。

图像的矩

图像的矩到底是什么?

矩是概率与统计中的一个概念,是随机变量的一种数字特征。

有点抽象,简而言之,矩就是图像的特征信息,比如大小、位置、方向等。

OpenCV提供了一些函数来计算图像的矩:

  • 矩的重心、主轴、面积等特征计算:moments()
  • 计算轮廓面积:contourArea()
  • 计算轮廓长度:arcLength()

下面的程序,使用了两种方法计算轮廓面积,第一种使用了moments()函数(程序里的mu[i].m00),第二种使用了contourAra()函数进行面积计算,大家可以看一下两种方法计算出来的面积有没有差别。

#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include <iostream>
#include <stdio.h>
#include <stdlib.h>using namespace cv;
using namespace std;Mat src; Mat src_gray;
int thresh = 100;
int max_thresh = 255;
RNG rng(12345);/// 函数声明
void thresh_callback(int, void*);/** @主函数 */
int main()
{/// 读入原图像, 返回3通道图像数据src = imread("lol10.jpg");/// 把原图像转化成灰度图像并进行平滑cvtColor(src, src_gray, CV_BGR2GRAY);blur(src_gray, src_gray, Size(3, 3));/// 创建新窗口char* source_window = "Source";namedWindow(source_window, CV_WINDOW_AUTOSIZE);imshow(source_window, src);createTrackbar(" Canny thresh:", "Source", &thresh, max_thresh, thresh_callback);thresh_callback(0, 0);waitKey(0);return(0);
}/** @thresh_callback 函数 */
void thresh_callback(int, void*)
{Mat canny_output;vector<vector<Point> > contours;vector<Vec4i> hierarchy;/// 使用Canndy检测边缘Canny(src_gray, canny_output, thresh, thresh * 2, 3);/// 找到轮廓findContours(canny_output, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, Point(0, 0));/// 计算矩vector<Moments> mu(contours.size());for (int i = 0; i < contours.size(); i++){mu[i] = moments(contours[i], false);}///  计算中心矩:vector<Point2f> mc(contours.size());for (int i = 0; i < contours.size(); i++){mc[i] = Point2f(mu[i].m10 / mu[i].m00, mu[i].m01 / mu[i].m00);}/// 绘制轮廓Mat drawing = Mat::zeros(canny_output.size(), CV_8UC3);for (int i = 0; i< contours.size(); i++){Scalar color = Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));drawContours(drawing, contours, i, color, 2, 8, hierarchy, 0, Point());circle(drawing, mc[i], 4, color, -1, 8, 0);}/// 显示到窗口中namedWindow("Contours", CV_WINDOW_AUTOSIZE);imshow("Contours", drawing);/// 通过m00计算轮廓面积并且和OpenCV函数比较printf("\t Info: Area and Contour Length \n");for (int i = 0; i< contours.size(); i++){printf(" * Contour[%d] - Area (M_00) = %.2f - Area OpenCV: %.2f - Length: %.2f \n", i, mu[i].m00, contourArea(contours[i]), arcLength(contours[i], true));Scalar color = Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));drawContours(drawing, contours, i, color, 2, 8, hierarchy, 0, Point());circle(drawing, mc[i], 4, color, -1, 8, 0);}
}

从结果看来,两种方法计算得到的面积是一样的。

OpenCV精进之路(八):图像轮廓和图像分割修复——轮廓查询和多边形包围轮廓相关推荐

  1. 《OpenCV3编程入门》学习笔记8 图像轮廓与图像分割修复(一)查找并绘制轮廓

    第8章 图像轮廓与图像分割修复 8.1 查找并绘制轮廓 8.1.1 寻找轮廓:findContours()函数 1.作用:在二值图像中寻找轮廓 2.函数原型: void findcontours(In ...

  2. OpenCV精进之路(十六):图像分解和融合技术——图像拼接和图像融合技术

    图像拼接在实际的应用场景很广,比如无人机航拍,遥感图像等等,图像拼接是进一步做图像理解基础步骤,拼接效果的好坏直接影响接下来的工作,所以一个好的图像拼接算法非常重要. 再举一个身边的例子吧,你用你的手 ...

  3. OpenCV与图像处理学习八——图像边缘提取(Canny检测代码)

    OpenCV与图像处理学习八--图像边缘提取(Canny检测代码) 一.图像梯度 1.1 梯度 1.2 图像梯度 二.梯度图与梯度算子 2.1模板卷积 2.2 梯度图 2.3 梯度算子 2.3.1 R ...

  4. OpenCV精进之路(九):图像轮廓和图像分割修复——图像修复技术

    在实际应用中,我们的图像常常会被噪声腐蚀,这些噪声或是镜头上的灰尘或水滴,或是旧照片的划痕,或者是图像遭到人为的涂画(比如马赛克)或者图像的部分本身已经损坏.如果我们想让这些受到破坏的额图片尽可能恢复 ...

  5. 《OpenCV3编程入门》学习笔记8 图像轮廓与图像分割修复(三)使用多边形将轮廓包围

    8.3 使用多边形将轮廓包围 8.3.1 将轮廓包围的多边形函数 1.返回外部边界:boundingRect()函数 (1)作用:返回指定点集最外面的边界矩形(四个顶点) (2)函数原型:Rect b ...

  6. OpenCV精进之路(二十):工具——图像标注小工具

    搞图像深度学习的童鞋一定碰过图像数据标注的东西,当我们训练网络时需要训练集数据,但在网上又没有找到自己想要的数据集,这时候就考虑自己制作自己的数据集了,这时就需要对图像进行标注.图像标注是件很枯燥又很 ...

  7. OpenCV精进之路(十七):工具——图像配准工具

    近日在做基于sift特征点的图像配准时遇到匹配失败的情况,失败的原因在于两幅图像分辨率相差有点大,而且这两幅图是不同时间段的同一场景的图片,所以基于sift点的匹配已经找不到匹配点了.然后老师叫我尝试 ...

  8. OpenCV精进之路(四):图像处理——图片的缩放和图像金字塔

    前言 对图像进行缩放的最简单方法当然是调用resize函数啦! resize函数可以将源图像精确地转化为指定尺寸的目标图像. 要缩小图像,一般推荐使用CV_INETR_AREA来插值:若要放大图像,推 ...

  9. OpenCV精进之路(零):访问图像中像素的三种方法

    访问像素的三种方法 指针访问:最快 迭代器iterator:较慢,非常安全,指针访问可能出现越界问题 动态地址计算:更慢,通过at()实现.适用于访问具体某个第i行,j列的像素,而不适用遍历像素 这里 ...

最新文章

  1. 如何高效读论文?剑桥CS教授亲授“三遍论”
  2. awk 实例练习(一)
  3. 公钥密码学标准(Public Key Cryptography Standards, PKCS)
  4. 创业第一天,有三AI扔出了深度学习的150多篇文章和10多个专栏
  5. iOS与Opencv的探秘之Opencv认识,适配Xcode
  6. Kotlin学习笔记——安装配置kotlin
  7. php读取某类型文件代码,php代码实现读取文件头判断文件类型
  8. 现代软件工程系列 学生和老师都不容易
  9. java怎么求方程的虚根_java怎么求一元二次方程虚根,虚根i怎么定义啊。
  10. centos安装python3小白_centos7安装python3
  11. jupyter notebook添加虚拟环境
  12. 来自Unix/Linux的编程启示录
  13. directive之require
  14. Android手机安装原版BT5[ARM]
  15. 223611-42-5,S-acetyl-PEG4-alcohol含有硫乙酰基的PEG连接剂
  16. python 时间格式datetime、str与date的相互转换
  17. 网络实验之EtherChannel技术实践
  18. invalid python sd,Fatal Python error: init_fs_encoding: failed to get the Python cod如何解决
  19. 前端json对比工具
  20. 吉林大学2013级大一下学期程序设计作业:同学通讯录系统

热门文章

  1. php 取oracle图片,一个php导出oracle库的php代码
  2. lisp 设计盘形齿轮铣刀_齿轮是怎么来的——图解6种齿轮加工工艺
  3. linux获取打开串口失败的原因,linux – 从串口读取失败
  4. mysql跳脱字符 单引号_MYSQL特殊字符(单引号,行尾斜杠)的处理
  5. 浅议“全局变量”、“多线程”和“编译器陷阱”
  6. ActionMapping
  7. telnet收发邮件
  8. virtual box和vmware有什么区别吗_真发假发套与普通假发有什么区别吗?
  9. 修改GDAL库支持RPC像方改正模型
  10. 30 分钟学 Erlang