1 击中击不中变换

1.1 HMT概述

形态学Hit-or-Miss是形状检测基本工具,只要结构元设置得当,就可以检测一些基本的形状图案,HMT变换只能作用于二值图像,结构元(核)元素值由0、1、-1组成。

操作时,结构元在图像上滑动,覆盖一小片与核大小一样的区域,然后逐一对比,核的值为1时,覆盖区域对应位置必须为255,而核值为-1时,则必须为0,核值为0时0和255均可,如果覆盖区域所有的位置均满足上述要求,则表示击中,锚点位置设置为255,如果有任意一个位置不满足,则表示击不中,锚点位置设置为0。

不过HMT单独用来作为通俗意义上的形状检测并不是很常用(个人理解),一般都是作为其他形态学算法的基础,例如:凸壳、细化、骨架化等。

1.2 代码实现

下面简单演示一下HMT的简单实现代码和固定尺寸矩形的检测

//func:          击中击不中变换:Hitmiss-----> 核中-1与图像中0对应,1与255对应,0任意对应
//matpadded:    输入已进行边界扩展的二值化图像
cv::Mat _hit_or_miss(const cv::Mat& matpadded, const cv::Mat& kernel)
{CV_Assert(matpadded.type() == CV_8UC1);CV_Assert(kernel.type() == CV_32SC1);           //含有负数,选int类型int rows = matpadded.rows - kernel.rows + 1;int cols = matpadded.cols - kernel.cols + 1;cv::Mat mat = cv::Mat::zeros(rows, cols, CV_8UC1);for (int i = 0; i < mat.rows; ++i){for (int j = 0; j < mat.cols; ++j){bool isGood = true;cv::Mat roi(matpadded, cv::Rect(j, i, kernel.cols, kernel.rows));//进行击中击不中判断for (int ii = 0; ii < kernel.rows; ++ii){uchar* roi_p = roi.ptr<uchar>(ii);const int* kernel_p = kernel.ptr<int>(ii);for (int jj = 0; jj < kernel.cols; ++jj){if ((kernel_p[jj] == 1 && roi_p[jj] == 0) || (kernel_p[jj] == -1 && roi_p[jj] == 255)){isGood = false;break;}}if (!isGood)break;}mat.ptr<uchar>(i)[j] = (isGood ? 255 : 0);}}return mat;
}/*
在测试图像中创建若干个4X4填充矩形,和边长大于4的矩形,利用HMT对4x4矩形进行检测
结构元设置为{-1,-1,-1,-1,-1,-1-1,1,1,1,1,-1-1,1,1,1,1,-1-1,1,1,1,1,-1-1,1,1,1,1,-1-1,-1,-1,-1,-1,-1};
即在矩形的外圈填充一圈-1,以此来确定矩形的边界。
*/int main()
{cv::Mat kernel = (cv::Mat_<int>(6, 6) <<-1, -1, -1, -1, -1, -1,-1, 1, 1, 1, 1, -1,-1, 1, 1, 1, 1, -1,-1, 1, 1, 1, 1, -1,-1, 1, 1, 1, 1, -1,-1, -1, -1, -1, -1, -1);cv::Size shapeSize = kernel.size();cv::Mat matPadded;cv::Mat myHitmiss, opencvHitmiss;//边界拓展的原则是:如果锚点在核中心,当核的尺寸为偶数时,左和上边界要比其他两边界多1int left = shapeSize.width / 2;int right = shapeSize.width % 2 == 0 ? shapeSize.width / 2 - 1 : shapeSize.width / 2;int top = shapeSize.height / 2;int bottom = shapeSize.height % 2 == 0 ? shapeSize.height / 2 - 1 : shapeSize.height / 2;//创建测试图像cv::Mat test = cv::Mat::zeros(300, 300, CV_8UC1);cv::rectangle(test, cv::Rect(20, 20, 4, 4), cv::Scalar(255), cv::FILLED);cv::rectangle(test, cv::Rect(50, 20, 4, 4), cv::Scalar(255), cv::FILLED);cv::rectangle(test, cv::Rect(20, 60, 4, 4), cv::Scalar(255), cv::FILLED);cv::rectangle(test, cv::Rect(60, 20, 4, 4), cv::Scalar(255), cv::FILLED);cv::rectangle(test, cv::Rect(80, 40, 6, 6), cv::Scalar(255), cv::FILLED);cv::rectangle(test, cv::Rect(100, 80, 10, 8), cv::Scalar(255), cv::FILLED);cv::copyMakeBorder(test, matPadded, top, bottom, left, right, cv::BORDER_REFLECT101);myHitmiss = _hit_or_miss(matPadded, kernel);cv::morphologyEx(test, opencvHitmiss, cv::MORPH_HITMISS, kernel);cv::imshow("myhitmiss", myHitmiss);cv::imshow("opencvHitmiss", opencvHitmiss);cv::waitKey(0);return 0;
}

1.3 结果展示

在结果图中形成了一些小白点,即检测到的规定尺寸大小矩形的中心


2 形态学细化

2.1 概述

在众多博客中看到了各种各样的细化版本,本人学识较浅,实在是读不懂别人的代码,就花了一点时间理解了一下冈萨雷斯《数字图像处理》形态学细化篇章,并做了一个代码实现。

形态学细化由我的理解来说就是:利用前辈们总结的一组结构元,不断循环重复的进行HMT变换,直至结果收敛(不在变换),单次细化公式定义为:

其中 A A A为源图像, B B B为结构元,编程时主要采用中间哪项定义即:用输出图 = 源图像 - 结构元对源图像进行HMT变换的结果。

其中 B B B为:

依据这一结构元序列将细化定义为:

这一处理过程就是 A A A被 B 1 B^1 B1细化一次,得到的结果然后被 B 2 B^2 B2细化一次,以此类推,一直套娃下去,直至得到的结果不在出现变化为止。


对于图片中的结构元,黑色代表前景值为1,白色为背景值为-1,x的值为0.

2.2 代码实现

只做简单实现,耗时在100ms左右。

//单次图像细化
//输入二值化图像
static void Morph_Thinning(const cv::Mat& src, const cv::Mat& kernel, cv::Mat& dst)
{CV_Assert(src.type() == CV_8UC1);CV_Assert(kernel.type() == CV_32SC1);cv::Mat tmpdst;cv::morphologyEx(src, tmpdst, cv::MORPH_HITMISS, kernel);dst = src - tmpdst;
}/*
对一副图像计算梯度幅值图像,将二值化后的幅值图像进行细化
*/int main()
{std::string path = "F:\\NoteImage\\扑克牌2.jpg";cv::Mat src = cv::imread(path, cv::IMREAD_GRAYSCALE);if (!src.data) {std::cout << "Could not open or find the image" << std::endl;return -1;}cv::Mat dx, dy;cv::Sobel(src, dx, CV_32FC1, 1, 0);cv::Sobel(src, dy, CV_32FC1, 0, 1);cv::Mat mag;cv::magnitude(dx, dy, mag);cv::normalize(mag, mag, 0, 255, cv::NORM_MINMAX);mag.convertTo(mag, CV_8UC1);cv::Mat thres;cv::threshold(mag, thres, 50, 255, cv::THRESH_BINARY);//创建结构元序列std::vector<std::vector<int>> Kernel_array = {{-1,-1,-1,0,1,0,1,1,1},{0,-1,-1,1,1,-1,1,1,0},{1,0,-1,1,1,-1,1,0,-1},{1,1,0,1,1,-1,0,-1,-1},{1,1,1,0,1,0,-1,-1,-1},{0,1,1,-1,1,1,-1,-1,0},{-1,0,1,-1,1,1,-1,0,1},{-1,-1,0,-1,1,1,0,1,1}};std::vector<cv::Mat> kernels(Kernel_array.size());for (int i = 0; i < Kernel_array.size(); ++i){cv::Mat kernel = cv::Mat(Kernel_array[i]).reshape(0, 3);kernels[i] = kernel;}int iterations = 0;                  //迭代次数int equalCount = 0;                  //收敛次数const int Max_Iterations = 100;      //最大迭代次数cv::Mat dst = thres.clone();double t = cv::getTickCount();while (iterations < Max_Iterations){cv::Mat tempdst = dst.clone();const int index = iterations % 8;Morph_Thinning(dst, kernels[index], dst);//判断这一次的结果和上一次是否相等cv::Mat diff = (tempdst != dst);bool equal = (cv::countNonZero(diff) == 0);if (equal)equalCount++;elseequalCount = 0;//收敛次数超过两次退出if (equalCount > 2)break;iterations++;}//计算时间打印结果double timepass = (cv::getTickCount() - t) / cv::getTickFrequency();std::cout << iterations << std::endl;std::cout << timepass << std::endl;cv::imshow("Thinning_dst", dst);cv::waitKey(0);return 0;
}

2.3 实验结果


看的出来边缘被明显细化,但要达到真正的1像素宽边缘,还要将细化后元素转化为m连通,这一步骤下期再见!

OpenCV实现击中击不中变换和形态学细化相关推荐

  1. 【youcans 的 OpenCV 例程 200 篇】120. 击中-击不中变换

    欢迎关注 『youcans 的 OpenCV 例程 200 篇』 系列,持续更新中 欢迎关注 『youcans 的 OpenCV学习课』 系列,持续更新中 [youcans 的 OpenCV 例程 2 ...

  2. 二值形态学之击中击不中变换

    定义 应用 物体识别 细化 击中击不中变换(Hit Miss Transform ,HMT),是通过同时探测图像的内部和外部,进而获取更多的内外标记,体现更多信息的一个方法.他的应用有很多,特别是在图 ...

  3. 击中击不中变换的作用理解

    学习形态学变换的时候,看到击中击不中变换一直百思不得其解,虽然他的定义写的很清楚明白,但是不知道具体是干嘛用的,按照自己的理解写了一个matlab小程序来试试. 1.击中击不中变换是形态学形状检测的基 ...

  4. 详解图像形态学中的击中击不中变换操作(HMT),并提醒大家OpenCV4中的击中击不中变换操作是有问题的

    大家看这篇博文前可以先看一看下面这篇博文,下面这篇博文是这篇博文的基础: 详解图像形态学操作之图形的腐蚀和膨胀的概念和运算过程,并利用OpenCV的函数erode()和函数dilate()对图像进行腐 ...

  5. 形态学 - 击中-击不中变换

    目录 1. HMT 介绍 2. 代码实现 1. HMT 介绍 HMT全称是Hit miss transformation --- 击中 击不中变换,这里我们只对二值图像做处理 二值图像是图像的灰度值只 ...

  6. 击中-击不中变换(约束)—lhMorpHMTC

    击中-击不中变换(约束) 所采用的9*1 自定义结构为: 函数:lhMorpHMTC 说明:形态学约束击中-击不中变换,当为二值图像时,结果与lhMorpHMTB相同. 参数: src 输入图像,灰度 ...

  7. 击中-击不中变换—lhMorpHMT

    函数:lhMorpHMT 说明:形态学击中-击不中变换 参数: src 输入图像,灰度或二值图像 dst 输出图像 sefg 前景结构元素 sebg 背景结构元素,如为空,则默认为前景结构元素sefg ...

  8. 区域填充与击中击不中变换

    1,区域填充 %区域填充 clear,clc,close all; BW1 = logical([1 0 0 0 0 0 0 0; 1 1 1 1 1 0 0 0;1 0 0 0 0 0 1 0; 1 ...

  9. 数字图像处理 击中击不中变换

    参考链接 目的: 击中击不中变换就是在A图像上找到和B图像一致的那块区域,举个例子就是:你拿着一张A图片的一部分,你需要在A图像上找到这张图片在A图片上的位置在哪(注意,这里找到的位置,都是一个点,也 ...

最新文章

  1. 交换机应用寻找10个完美的因素
  2. Git 头指针分离与 FETCH_HEAD
  3. 中国互联网+果汁行业商业模式创新与投资机会深度研究报告
  4. C# 工厂模式 简单入门
  5. linux组权限管理,linux 用户组权限管理(示例代码)
  6. Kafka学习-复制
  7. Android 经典示例,初学者的绝好源码资料
  8. 如何生成高性能的短链接?
  9. BeginnersBook Java 集合教程
  10. 司机行为识别_台州交警上线“闯红灯抓拍神器”人脸识别+4米大屏,拍到违章直接曝光...
  11. 有没有这样一种程序员写代码的利器
  12. android 扩展textview,Android可收缩/扩展的TextView【1】
  13. 第一章概述-------第一节--1.2互联网概述
  14. 一起Talk Android吧(第三百一十七回:Android中的虚拟按键)
  15. 12.Oracle Redo重做日志管理
  16. 后浪云WWW.IDC.NET:裸金属服务器有什么优势?
  17. js 根据中文获取拼音首字母
  18. php对参数校验(名称、地址、掩码、日期、时间、端口)
  19. 判断用户输入的是否是正确电话号码
  20. 行为型:设计模式之访问者模式(二十三)

热门文章

  1. 一文看尽CES Asia:大变革下的汽车与全面AI化的智能家居
  2. 【自由探索】决策树归纳
  3. 作为程序员的你,这10款在线编辑器,你用过哪一个?
  4. 浙江大学ACM俱乐部 1036:镂空三角形
  5. STL 常用容器的底层数据结构
  6. unicode编码 中文标点符号
  7. 电脑的ppt打不开计算机二级,打不开电脑中的ppt文件并提示访问出错的解决方法...
  8. ideaij 按内容查找文件
  9. 【Note】微信小程序js使用农历(一行代码)
  10. 照片模糊怎么变清晰?