专栏首页微卡智享【干货】C++ OpenCV案例实战---卡片截取(附代码

【干货】C++ OpenCV案例实战---卡片截取(附代码

前言

我们已经更新了不少OpenCV的基础文章了,为了巩固一下学习效果,我们就做的小案例的实战进行强化。

实现效果

今天我照了一张自己健身卡的图片,然后想到实现整体切边的效果,先上一下源图

最终我们想到实现在照片中只保留卡这块的部分。

代码演示

我们再新建一个项目名为opencv--qiebian,按照配置属性(VS2017配置OpenCV通用属性),然后在源文件写入#include和main方法.

上面我们把加载出来的图片用resize来调整了一下大小,一开始加载出来超过满屏了,不方便我们处理。


第一步 先把图片转为灰度图

显示效果为


第二步 用Canny边缘提取

我们来看看显示效果

可以看到右边就是我们通过边缘提取后的图片效果


第三步 发现寻找轮廓

运行后再看看效果

可以看出来,我们边缘提取后找到了1332个轮廓,这个轮廓中我们只需要找到我们想到的卡信息,所以我们就来到下一步


第四步 定义最小的轮廓宽高,画出想要的轮廓

可以看到图中卡片占了图片一半以上,所以我们可以直接考虑轮廓的大小大于源图像一半就是我们要少的轮廓了,其余的轮廓就可以忽略了,这样可以节省很多时间,代码如下:

然后我们看看运行效果

可以看出,我们要找到矩形完全不是我们想要的结果,在红色的轮廓里面发现卡片和桌面上的纹理连到一起了,说明我们在Canny边缘提取的时候需要进行二值化处理,去掉一些不用的结果


第五步 对源图像进行Canny阈值重新修改看看

我们回到Canny边缘提取那,把边缘提取的阈值重修改一下

然后我们重新运行起来看看什么效果

发现右边通过赋值后不像原来桌面上那么多纹理了,但是从左边获取的矩形框后发现也完全不是我们想要的东西。要怎么解决这个问题呢?

一开始我想过了在Canny边缘提取前先二值化操作一下,最终倒是实现了,但是这个要不停的修改阈值参数,不太方便,还有别的什么办法呢?这时候我想起来,刚开始学最基本的形态学操作的时候用到的,开操作和闭操作。我们这就来试一下,根据上面的情况,我选择的闭操作,也就是先膨胀后腐蚀的效果

然后我们再来看看运行的效果

经过闭操作后,可以看出右边的图我们过滤掉了非常多没有必要的东西,也获取到了最后我们的绿色画出的矩形框,自我满足了一下!!!!


第六步 提取到我们要的图像

我们先修改一下上面定义的参数,这样下在我们截取的时候就可以用到这个矩形了,下面是我们在复制一份src_gray,因为原图中src我们已经画上红色和绿色线了,画之前先存一个备份到src_gray里。

然后我们提取最后生成的矩形

最后我们看看生成的结果

右边就是我们完美截取的卡片图像,成功~~~~


总结一下实现流程

  1. 加载图片
  2. 转为灰度图
  3. 图像高斯模糊
  4. 进行闭操作(先膨胀后腐蚀)
  5. Canny边缘提取
  6. 寻找轮廓
  7. 轮廓中查找符合要求的项
  8. 获取上一步中对应项的最小矩形
  9. 从源图像中截取最小矩形生成新图片

以上是我自己实验生成的效果,如果有更好的方法请消息我,毕竟我也是初学者~~


main单元代码

#include <opencv2\opencv.hpp> #include <iostream> cv::Mat src, src_gray, dst; const char* imgsrc = "源图"; const char* imgdst = "结果图"; int main(int argc, char** argv) { src = cv::imread("E:/DCIM/testcard.jpg"); if (src.empty()) { printf("could not read image....\n"); getchar(); return -1; } //由于加载图像有点大,我们先调整图像的大小为宽500,高300 cv::resize(src, src, cv::Size(500, 300)); cv::imshow(imgsrc, src); //把源图转为灰度图 cv::cvtColor(src, src_gray, CV_BGR2GRAY); cv::imshow(imgdst, src_gray); //高斯模糊后加Canny边缘提取 cv::GaussianBlur(src_gray, dst, cv::Size(3, 3), 0.5, 0.5, 4); //定义结构元素 cv::Mat element = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5), cv::Point(-1, -1)); //闭操作 cv::morphologyEx(dst, dst, CV_MOP_CLOSE, element); //Canny边缘提取 cv::Canny(dst, dst, 120, 255); //定义轮廓点及查找轮廓 std::vector<std::vector<cv::Point> > contours; std::vector<cv::Vec4i> hierarchy; //寻找轮廓 cv::findContours(dst, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE); //打印出一共找到了多少个轮廓 printf("contours count:%d\n", contours.size()); //定义要找的轮廓最小的宽高 int minw = dst.cols*0.5; int minh = dst.rows*0.5; //定义矩形的四个点 cv::RotatedRect minRect; cv::Point2f vertices[4]; for (size_t t = 0; t < contours.size(); t++) { //寻找最小矩形 minRect = cv::minAreaRect(contours[t]); //获取倾斜角度 double degree = abs(minRect.angle); //判断最小矩形太于我们设置的最小宽和高在获取数据 if (minRect.size.width > minw && minRect.size.height > minh) { printf("current rect: %d\n", t); printf("current angle : %f\n", degree); //在源图像上用红色画出轮廓样 src.copyTo(src_gray);//先复制一份src到src_gray里面,下面备用 cv::drawContours(src, contours, t, cv::Scalar(0, 0, 255), 1,8,hierarchy,0); //在源图像上用绿色画出矩形框,定义了4个点,然后用直线画4个点 minRect.points(vertices); for (int i = 0; i < 4; i++) { cv::line(src, vertices[i], vertices[(i + 1) % 4], cv::Scalar(0, 255, 0)); } break; } } cv::imshow(imgsrc, src); //根据获取的最小矩形截取出图像显示出来 cv::Rect rect = minRect.boundingRect(); dst = src_gray(rect); cv::imshow(imgdst, dst); cv::waitKey(0); return 0; }


-END-

本文分享自微信公众号 - 微卡智享(VaccaeShare),作者:Vaccae

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2018-09-19

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 【干货】C++ OpenCV案例实战---卡片截取(旋转取卡)

    前面一章《【干货】C++ OpenCV案例实战---卡片截取(附代码)》我们通过实战练习了怎么截取卡片信息,但是如果遇到了图片中卡片不是正方向的话我们就截取不了...

    Vaccae
  • 【干货】C++ OpenCV案例实战---卡号获取

    前面我们学习了《【干货】C++ OpenCV案例实战---卡片截取(附代码)》,根据照出来的照片直接截取到卡片后,在卡片识别里面下一步我们肯定就会用到了卡号的获...

    Vaccae
  • 【综合练习】C++ OpenCV实战---获取数量

    前阵子做了一个实战分享《【干货】C++ OpenCV案例实战---卡片截取(附代码)》,今天我们再把以前学习到的东西综合练习一下,做一个获取个数的小案例。

    Vaccae
  • C++ | PaddleOCR GPU版使用步骤与测试时间对比(相对CPU)

    本文主要介绍C++版PaddleOCR GPU版的使用步骤和测试时间对比(相对CPU)。

    Color Space
  • 灵魂追问 | 教程那么多,你……看完了吗?

    机器之心
  • C#之反射、元数据详解

      在本节中主要讲述自定义特性、反射 。自定义特性允许把自定义元数据与程序元素关联起来。这些元数据是在编译过程中创建的,并嵌入程序集中。反射是一个普通的术语,它...

    Vaccae
  • 一行代码实现灰色铅笔画、彩色铅笔画和卡通画效果(C++/Python OpenCV源码)

    本文主要介绍在OpenCV中如何使用一行代码实现图像转灰色铅笔画、彩色铅笔画和卡通效果。

    Color Space
  • 全套 | 人脸检测 & 人脸关键点检测 & 人脸卡通化

    可能跟我一样,人脸检测是很多人学习图像处理的第一个自驱动型的任务,OpenCV刚上手没几天可能就想先跑一跑人脸检测,然后一个坑接着一个坑的往里跳。我个人对人脸检...

    AI算法与图像处理
  • C+实现神经网络之三—神经网络的训练和测试

    前言 在之前的博客中我们已经实现了Net类的设计和前向传播和反向传播的过程。可以说神经网络的核心的部分已经完成。接下来就是应用层面了。要想利用神经网络解决实际的...

    企鹅号小编
  • 18本纸质书:OpenCV、Python和机器学习,总有一本适合你

    这次主页君蒙电子工业出版社赞助,为大家准备了6个三本:包含OpenCV类书籍四本,机器学习类书籍两本,每本书送出三份,一共十八个名额。这六种书籍都是干货满满的...

    企鹅号小编
  • Airtest图像识别

    Airtest是一款网易出品的基于图像识别面向手游UI测试的工具,也支持原生Android App基于元素识别的UI自动化测试。主要包含了三部分:Airtest...

    腾讯移动品质中心TMQ
  • 实战 | 粘连物体分割与计数应用(一)--基于形态学+连通域处理 Halcon/OpenCV实现比较

    本文主要介绍基于基于形态学+连通域处理实现粘连物体的分割与计数方法,并对比Halcon与OpenCV实现差异。

    Color Space
  • 【IoT应用创新大赛】基于LoRa与机械臂的家居系统

    物联网是一种连接各种各样的传感器的网络,与之对应的是20世纪60年代开始研究的计算机网络,后者将分散于不同地理位置的计算机连接起来。物联网传感器可以采集的信息包...

    羽翰尘
  • C++ OpenCV SVM实战Kindle检测(二)----目标检测

    前一篇文章《C++ OpenCV SVM实战Kindle检测(一)----训练数据》我们除了介绍了一下SVM,并且做了对Kindle的图片进行了数据的训练,生成...

    Color Space
  • 13行代码实现:Python实时视频采集(附源码)

    本文是《人脸识别完整项目实战》系列博文第3部分:程序设计篇(Python版),第1节《Python实时视频采集程序设计》,本章内容系统介绍:基于Python+o...

    数据饕餮
  • 多模态深度学习:用深度学习的方式融合各种信息

    我们对世界的体验是多模态的 —— 我们看到物体,听到声音,感觉到质地,闻到气味,尝到味道。模态是指某件事发生或经历的方式,当一个研究问题包含多个模态时,它就具有...

    小白学视觉
  • 转发有礼 | 50篇+云原生系列干货文章汇总,请查收!

    云原生技术干货文章合集,来咯~ 2020 年,要说咱们技术圈子里什么最火? 云原生肯定是那 NO.1 截止目前,我们不难看出,K8s 容器、服务网格、...

    腾讯云原生
  • C+实现神经网络之四—神经网络的预测和输入输出的解析

    在上一篇的结尾提到了神经网络的预测函数predict(),说道predict调用了forward函数并进行了输出的解析,输出我们看起来比较方便的值。 神经网络的...

    企鹅号小编
  • C++ OpenCV SVM实战Kindle检测(一)----训练数据

    最近也是在接触机器学习,通过做了几个MLNET的例子对机器学习有了一点了解,OpenCV中也有机器学习这块,所以我们就直接来用Kindle做一个实战。

    Vaccae

扫码关注云+社区

领取腾讯云代金券