准确检测图像的轮廓 opencv_图像处理案例实战

0260f96f-4a13-eb11-8da9-e4434bdf6706.png

1. 切边

源图像:

0460f96f-4a13-eb11-8da9-e4434bdf6706.png

需求:扫描仪扫描到的法律文件,需要切边,去掉边缘空白,这样看上去才真实,人工操作成本与时间花费高,希望程序自动实现,高效、准确。 实现思路:边缘检测 + 轮廓发现或直线检测最大外接矩形。

例子代码:

#include

效果图

0660f96f-4a13-eb11-8da9-e4434bdf6706.png

总结:先利用 Canny 算子检测图像的轮廓,再利用 findContours 发现轮廓,因为这时候会得到很多轮廓,而我们只需要找到最大的轮廓,这时候可以根据源图像的情况设置过滤的条件(如本例中宽高设为不小于0.75)。找到这个矩形后,再把它画出来即可。

2. 直线检测

源图像:

0960f96f-4a13-eb11-8da9-e4434bdf6706.png

需求:寻找英语试卷填空题的下划线,这个对后期的切图与自动识别都比较重要。 实现思路:通过图像形态学操作来寻找直线,霍夫获取位置信息与显示

例子代码:

#include<opencv2/opencv.hpp>
#include<iostream>
using namespace cv;
using namespace std;
Mat srcImg, roiImg;
void detectLine();
void test()
{srcImg = imread("test2.jpg",IMREAD_GRAYSCALE);if (srcImg.empty()){cout << "could not load image...n" << endl;}namedWindow("Original image", CV_WINDOW_NORMAL);  //CV_WINDOW_NORMAL 鼠标控制显示窗口的大小imshow("Original image", srcImg);//因为是截图,所以要去掉白边,用ROI图像的方法Rect roi = Rect(10, 10, srcImg.cols - 15, srcImg.rows - 15);  //切掉白边roiImg = srcImg(roi);namedWindow("Roi image", CV_WINDOW_NORMAL);imshow("Roi image", roiImg);detectLine();
}void detectLine()
{
Mat binaryImg, morhpImg;threshold(roiImg, binaryImg, 0, 255, THRESH_BINARY_INV | THRESH_OTSU);  //二值化namedWindow("Binary Result", CV_WINDOW_NORMAL);imshow("Binary Result", binaryImg);//形态学操作Mat kernel = getStructuringElement(MORPH_RECT, Size(60, 1), Point(-1, -1));  morphologyEx(binaryImg, morhpImg, MORPH_OPEN, kernel, Point(-1, -1));namedWindow("Morphology Result", CV_WINDOW_NORMAL);imshow("Morphology Result", morhpImg);//膨胀,使得直线更加明显kernel = getStructuringElement(MORPH_RECT, Size(3, 3), Point(-1, -1));dilate(morhpImg, morhpImg, kernel);namedWindow("Dilate Result", CV_WINDOW_NORMAL);imshow("Dilate Result", morhpImg);//霍夫变换标定直线vector<Vec4i> lines;   //每条线有两个点,把四个点的坐标存储起来HoughLinesP(morhpImg, lines, 1, CV_PI / 180.0, 30, 20.0, 0);  // 1 为极坐标方向上的步长Mat resultImg = roiImg.clone();   //输出结果cvtColor(resultImg, resultImg, COLOR_GRAY2BGR);for (int i = 0; i < lines.size(); i++){Vec4i ln = lines[i];  //取出直线line(resultImg, Point(ln[0], ln[1]), Point(ln[2], ln[3]), Scalar(0, 0, 255), 2, 8, 0);  //Point(ln[0], ln[1]), Point(ln[2], ln[3])两个坐标}namedWindow("Hough Lines-Final Result", CV_WINDOW_NORMAL);imshow("Hough Lines-Final Result", resultImg);
}
int main()
{test();waitKey(0);return 0;
}

效果图

0b60f96f-4a13-eb11-8da9-e4434bdf6706.png

总结:因为源图像是截图,所以最外围有白边,通常这种情况下可以利用 ROI 图像方法去掉不需要的部分,再通过二值化和形态学操作来寻找直线,最后利用霍夫概率变换获取直线的准确位置。

3. 对象提取

源图像:

0c60f96f-4a13-eb11-8da9-e4434bdf6706.png

需求:对图像中对象(圆形)进行提取,获取这样的对象,去掉其它干扰和非目标对象。并获取圆形的面积和周长。 实现思路:二值分割 + 形态学 + 横纵比计算。

步骤: - 找到对象的轮廓 - 通过面积、横纵比过滤点其它不需要的对象

第一步:发现轮廓 例子代码:

#include<opencv2/opencv.hpp>
#include<iostream>
using namespace cv;
using namespace std;
//第一步
Mat srcImg, binaryImg, dstImg;
void test()
{srcImg = imread("case3.png", IMREAD_GRAYSCALE);if (srcImg.empty()){cout << "could not load image...n" << endl;}namedWindow("Original image", CV_WINDOW_NORMAL);imshow("Original image", srcImg);//二值化threshold(srcImg, binaryImg, 0, 255, THRESH_BINARY | THRESH_OTSU);namedWindow("Binary Result", CV_WINDOW_NORMAL);imshow("Binary Result", binaryImg);//形态学操作,开操作,去掉小的对象,闭操作,连接里面的洞(开闭操作要先获得结构元素)Mat kernel = getStructuringElement(MORPH_RECT, Size(3, 3), Point(-1, -1));  //Point(-1, -1)是中心点,这里是 2 x 2位置morphologyEx(binaryImg, dstImg, MORPH_CLOSE, kernel, Point(-1, -1));namedWindow("Close Result", CV_WINDOW_NORMAL);imshow("Close Result", dstImg);kernel = getStructuringElement(MORPH_RECT, Size(3, 3), Point(-1, -1));  morphologyEx(dstImg, dstImg, MORPH_OPEN, kernel, Point(-1, -1));namedWindow("Open Result", CV_WINDOW_NORMAL);imshow("Open Result", dstImg);//轮廓发现vector<vector<Point>> contours;  //存储轮廓vector<Vec4i> hireachy;findContours(dstImg, contours, hireachy, RETR_TREE, CHAIN_APPROX_SIMPLE, Point());Mat resultImg = Mat::zeros(srcImg.size(), CV_8SC3);for (int i = 0; i < contours.size(); i++){drawContours(resultImg, contours, i, Scalar(0, 0, 255), 2, 8, Mat(), 0, Point());  //画出来}namedWindow("Final Result", CV_WINDOW_NORMAL);imshow("Final Result", resultImg);
}
int main()
{test();waitKey(0);return 0;
}

效果图

1260f96f-4a13-eb11-8da9-e4434bdf6706.png

第二步:过滤并计算面积和周长 例子代码:

#include<opencv2/opencv.hpp>
#include<iostream>
using namespace cv;
using namespace std;
//第二步 通过面积过滤,通过纵横比的测量,圆形的纵横比应该在1:1左右,如果不是1:1,就把它过滤掉
Mat srcImg, binaryImg, dstImg;
void test()
{srcImg = imread("case3.png", IMREAD_GRAYSCALE);if (srcImg.empty()){cout << "could not load image...n" << endl;}namedWindow("Original image", CV_WINDOW_NORMAL);  //CV_WINDOW_NORMAL 使得鼠标可以控制显示窗口的大小imshow("Original image", srcImg);//二值化threshold(srcImg, binaryImg, 0, 255, THRESH_BINARY | THRESH_OTSU);namedWindow("Binary Result", CV_WINDOW_NORMAL);imshow("Binary Result", binaryImg);//形态学操作,开操作,去掉小的对象,闭操作,连接里面的洞(开闭操作要先获得结构元素)Mat kernel = getStructuringElement(MORPH_RECT, Size(3, 3), Point(-1, -1));  //Point(-1, -1)是中心点,这里是 2 x 2位置morphologyEx(binaryImg, dstImg, MORPH_CLOSE, kernel, Point(-1, -1));namedWindow("Close Result", CV_WINDOW_NORMAL);imshow("Close Result", dstImg);kernel = getStructuringElement(MORPH_RECT, Size(3, 3), Point(-1, -1));morphologyEx(dstImg, dstImg, MORPH_OPEN, kernel, Point(-1, -1));namedWindow("Open Result", CV_WINDOW_NORMAL);imshow("Open Result", dstImg);//轮廓发现vector<vector<Point>> contours;  //存储轮廓vector<Vec4i> hireachy;findContours(dstImg, contours, hireachy, RETR_TREE, CHAIN_APPROX_SIMPLE, Point());//通过面积过滤,通过纵横比的测量,圆形的纵横比应该在1:1左右,如果不是1:1,就把它过滤掉Mat resultImg = Mat::zeros(srcImg.size(), CV_8SC3);Point cc;for (int i = 0; i < contours.size(); i++){//面积过滤double area = contourArea(contours[i]);  //循环获取指定的面积if (area < 100)  //通过循环过滤掉小于100的面积continue;//横纵比过滤Rect rect = boundingRect(contours[i]);float ratio = float(rect.width) / float(rect.height);if (ratio < 1.1 && ratio> 0.9)  //把满足条件的保留画出来{drawContours(resultImg, contours, i, Scalar(0, 0, 255), -1, 8, Mat(), 0, Point());  //画出来 第五个参数改为 -1 ,使得整个圆形填充cout << "circle area: " << area << endl;  //面积和周长打印出来(像素度量)cout << "circle length: " << arcLength(contours[i],true) << endl;//找中心点int x = rect.x + rect.width / 2;int y = rect.y + rect.height / 2;cc = Point(x, y);circle(resultImg, cc, 2, Scalar(0, 0, 255), 2, 8, 0);  //画出中心点}}namedWindow("Final Result", CV_WINDOW_NORMAL);imshow("Final Result", resultImg);//在原图上定位显示中心点Mat circleImg = srcImg.clone();cvtColor(circleImg, circleImg, COLOR_GRAY2BGR); circle(circleImg, cc, 2, Scalar(0, 0, 255), 2, 8, 0);namedWindow("Center Point", CV_WINDOW_NORMAL);imshow("Center Point", circleImg);
}int main()
{test();waitKey(0);return 0;
}

效果图

1360f96f-4a13-eb11-8da9-e4434bdf6706.png

总结:首先把图像二值化,通过闭操作把中间小的洞连接上,再通过开操作把周围小的点去掉,再通过轮廓发现找到对象的轮廓。再通过面积、纵横比过滤点其它不需要的对象,圆形的纵横比应该在1:1左右,如果不是1:1,就把它过滤掉。然后再计算面积和周长。


欢迎关注我的微信公众号“OpenCV图像处理算法”,主要是记录自己学习图像处理算法的历程,包括特征提取、目标跟踪、定位、机器学习和深度学习,每一个例子都会提供源码和例子所用的资料,欢迎同行的同学关注我和我一起虚度光阴吧!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/435518.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

O_EXCL

open系统调用&#xff1a; #include <fcntl.h>int open(const char *pathname, int oflag, ... /*mode_t mode */ );其中&#xff0c;oflag有个可选值为&#xff1a; O_EXCL Generate an error if O_CREAT is also specified and the file already exists. This test fo…

sql 解锁被锁定的账号

账号被锁定&#xff0c;在属性那里直接取消lock-out无法解锁。运行这条命令即可&#xff1a;ALTER LOGIN UserLogin WITH PASSWORD NewPassword UNLOCK MUST_CHANGE 转载于:https://www.cnblogs.com/bigshot-z/archive/2010/07/12/1775856.html

ubuntu18分辨率变低,无法调整,最高只有1024*768

方法一 运行xrandr&#xff0c;判断哪个显示器分辨率最大&#xff0c;那个就是真实的显示器 记住是LVDS1还是VGA1这里记住显示的名称 如果是两个&#xff0c;那么恭喜你可以继续设置后 应该就好&#xff0c;只有一个请使用方法二 具体步骤&#xff1a; 下载arandr sudo apt-g…

pp助手苹果版_再见!PP助手iOS端即将下线 曾是中国最大的苹果助手

苹果PP助手即将下线 在今日PP助手官方发布公告&#xff1a;尊敬的PP助手iOS版用户&#xff1a;衷心感谢您多年以来对我们的支持及厚爱。因业务调整&#xff0c;PP助手将于2020年2月28日正式下线iOS版产品&#xff0c;包括PP助手iOS版、PP助手iOS PC版等。 对您造成的不便还望理…

关于规划修编标准的问题

关于规划修编标准的问题 此问题只对规划修编数据库标准&#xff08;报批稿&#xff09;进行的分析!(1)标准的图形表字段和字段的值要可以推导出指标表格&#xff0c;且推导公式明确和正确&#xff1b;(2)地类编码体系要统一&#xff0c;且唯一&#xff0c;要有地类名称和地类代…

【转】Xrandr (简体中文)

转自&#xff1a;Xrandr (简体中文) - ArchWiki "xrandr" 是一款官方的 RandR (Resize and Rotate)维基百科&#xff1a;X 窗口系统 扩展配置工具。它可以设置屏幕显示的大小、方向、镜像等。对多显示器的情况&#xff0c;请参考 Multihead 页面。 Contents 1安装 …

oppo手机工程模式清除数据需要密码_手机隐藏的快捷键都有哪些?

手机从开发出来到现在&#xff0c;已经换了很多代了&#xff0c;从大哥大&#xff0c;小灵通&#xff0c;到诺基亚到山寨机到苹果到华为&#xff0c;几十年过去了。手机从奢侈品一下飞去每个人手里&#xff0c;反正我手机有手机已经快10年了&#xff0c;所以我相信每个人对自己…

关于竖表转横表的问题

本文作者&#xff1a;dinya内容摘要&#xff1a;在开发过程&#xff0c;经常遇到一些将表的显示方式进行转换的需求&#xff0c;我们习惯性称之为竖表到横表的转换&#xff0c;本文通过一个例子来简要说明常见的两种竖表转横表的问题。本文适宜读者范围&#xff1a;oracle初级&…

计算机大小端

大端模式   所谓的大端模式&#xff0c;是指数据的高位&#xff0c;保存在内存的低地址中&#xff0c;而数据的低位&#xff0c;保存在内存的高地址中&#xff0c;这样的存储模式有点儿类似于把数据当作字符串顺序处理&#xff1a;地址由小向大增加&#xff0c;而数据从高位往…

全系光谱完整版

宇宙射线也叫电磁波&#xff0c;其中包含γ射线、X射线、紫外线、可见光、红外线、近红外、远红外&#xff0c;还有无线电和超声波。 无线电波是振荡电路中自由电子作周期性的运动产生的&#xff0e; 红外线、可见光、紫外线是原子外层电子受激发产生的&#xff0e; X射线是原…

cat 几行_迷风花说Cat!Cat因为林教练是好教练回去现实却给了一巴掌

滔博TES前主教练迷风花26日发布了微博&#xff0c;就自己离职、Cat林教练事件以及汤汤纵情转会事件进行了澄清&#xff0c;网友粉丝们在看到教练迷风花发布的微博后&#xff0c;更加了解了很多事情的真相&#xff0c;粉丝们纷纷对教练迷风花送上了祝福&#xff0c;教练迷风花到…

c# equals与==的区别

对于值类型&#xff0c;如果对象的值相等&#xff0c;则相等运算符 () 返回 true&#xff0c;否则返回 false。对于string 以外的引用类型&#xff0c;如果两个对象引用同一个对象&#xff0c;则 返回 true。对于 string 类型&#xff0c; 比较字符串的值。操作比较的是两个变…

linux下C语言中的flock函数用法

表头文件 #include<sys/file.h> 定义函数 int flock(int fd,int operation); 函数说明 flock()会依参数operation所指定的方式对参数fd所指的文件做各种锁定或解除锁定的动作。此函数只能锁定整个文件&#xff0c;无法锁定文件的某一区域。 参数 operation有下列四种…

c# - C#用fo-dicom对CT图像的PixelData进行处理和转换

转自&#xff1a;c# - C#用fo-dicom对CT图像的PixelData进行处理和转换 - IT工具网 对于某些测试&#xff0c;我试图操纵 PixelData以 dicom 格式存储的 CT 图像的元素&#xff0c;并使用 C# 中的 Fellow Oak Dicom 将其写回文件中。经过一番研究&#xff0c;我发现我想要处理的…

android studio 显示图形_显示服务器实现(一)

在一个多窗口的图形化操作系统中&#xff0c;显示服务器的任务就是组织这些窗口展现给用户。实际的显示服务器有Linux世界基于X11协议的XServer或者Wayland协议的Weston&#xff0c;还有Android里的SurfaceFlinger。但是除了显示&#xff0c;他们同时包含了输入管理&#xff08…

discuzX 帖子 有的图片没输出 [attach]12323[/attach]的解决办法

在pre_forum_post (as p )中&#xff0c;每个帖子都有一个p.attachment字段&#xff0c;这个字段记录了&#xff0c;帖子中有多少张图片&#xff0c;如果为0&#xff0c;则程序不会替换帖子内容(p.message)中的[attach]标签&#xff0c; 在pre_forum_attachment (as a)中&#…

win32 API函数大全

1. API之网络函数 WNetAddConnection 创建同一个网络资源的永久性连接 WNetAddConnection2 创建同一个网络资源的连接 WNetAddConnection3 创建同一个网络资源的连接 WNetCancelConnection 结束一个网络连接 WNetCancelConnection2 结束一个网络连接 WNetCloseEnum 结束…

如何识别C++编译以后的函数名

C/C语言在编译以后&#xff0c;函数的名字会被编译器修改&#xff0c;改成编译器内部的名字&#xff0c;这个名字会在链接的时候用到。如果用backtrace之类的函数打印堆栈时&#xff0c;显示的就是被编译器修改过的名字&#xff0c;比如说_Z3foov 。 那么这个函数真实的名字是什…

聊聊临时表

什么是临时表临时表与永久表相似&#xff0c;但临时表存储在 tempdb 中&#xff0c;当不再使用时会自动删除。 临时表有两种类型&#xff1a;本地和全局。它们在名称、可见性以 及可用性上有区别。本地临时表的名称以单个数字符号 (#) 打头&#xff1b;它们仅对当前的用户连接是…

电脑不能开机维修_MacBook维修 苹果笔记本电脑进水不开机

最近有很多小伙伴来店里&#xff0c;是进水的Macbook苹果笔记本电脑开不开机、黑屏&#xff0c;有很多朋友认为Macbook进水后擦干&#xff0c;只要能开机就没事&#xff0c;实在不开机晾几天就会好了。其实不然。Macbook进水后晾干虽然能开机但有很多后遗症——比如忽然黑屏、反…