图像处理之细化算法(Thinning Algorithm)C++实现

最近在实验室里遇到了一个问题,就是在有一张轮廓二值图的情况下,如何才能将轮廓进行细化,得到轮廓的骨架。

效果如图:

image.png

image.png

可以看到,右边图中的数字变瘦了,这就是细化算法的作用

下面我们来讲一下,Thining-Algorithm的算法原理。

一、八领域

我们先来介绍一下,八领域这一个概念

image.png

如图,八领域是指包围了中心P1像素的八个像素点。在很多图像处理算法中,八领域的这个概念都极为常见,应用十分广泛。

二、算法原理

首先,我们来看一下以下的几个类型点:

image.png

可以看到,我们是通过八领域中的值,来确定这一点是内部点还是端点和孤立点,从而确定是否保留该点的像素值。


第一步:遍历考察所有的非零点,看是否满足一下四个条件:


   a. 2<= p2+p3+p4+p5+p6+p7+p8+p9<=6


   b. p2->p9的排列顺序中,01模式的数量为1,比如下面的图中,有p2p3 => 01, p6p7=>01,所以该像素01模式的数量为2。

image.png

之所以要01模式数量为1,是要保证删除当前像素点后的连通性。

 c. p2*p4*p6 = 0


    d. p4*p6*p8 = 0


将满足以上四个条件的点删除(像素值置为0)


第二步:还是通过四个条件来判断点的去留


a. 2<= p2+p3+p4+p5+p6+p7+p8+p9<=6


b. p2->p9的排列顺序中,01模式的数量(这里假设二值图非零值为1)为1。


c. p2*p4*p8 = 0


d. p2*p6*p8 = 0


可以看到其实在本质上,两大步骤中四个条件并没有很大的区别,只是在c、d两个条件上变成不同的方向。


合并c、d两个条件就可以看到,只需要p2、p4、p6、p8四个像素值有一个为零,中心像素p1就该删除。


好了,细化算法的原理就是这么多,其实还是比较简单的,但是有一点需要注意的是,我们在C++上编程,应该注意,其实不用将图片进行归一化,其实只需要简答的将所有的值乘以255就可以了,这样更加方便简单。


下面是源码:

#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <iostream>
 
using namespace std;
using namespace cv;
int main()
{
	//读入图像
	cv::Mat img = cv::imread("in.png");
	cv::Mat gray;
	cv::Mat Binary;
	//进行二值化
	cv::cvtColor(img, gray, CV_BGR2GRAY);
	cv::inRange(gray, 200, 255, Binary);
	cv::Mat CopyImg;
	int rows = Binary.rows;
	int cols = Binary.cols;
 
 
	std::vector<cv::Point2l> PointSaver1;
	//开始第一轮判断
	for (int i{ 1 };i<rows-1;i++)
	{
		uchar* high = Binary.ptr<uchar>(i-1);
		uchar* mid = Binary.ptr<uchar>(i);
		uchar* low = Binary.ptr<uchar>(i+1);
 
		for (int j{1};j<cols-1;j++)
		{
			int a1 = mid[j];
			int a2 = high[j];
			int a3 = high[j+1];
			int a4 = mid[j+1];
			int a5 = low[j+1];
			int a6 = low[j];
			int a7 = low[j-1];
			int a8 = mid[j-1];
			int a9 = high[j-1];
			int a[9] = { a1,a2,a3,a4,a5,a6,a7,a8,a9 };
 
			bool req1 = true;
			bool req2 = true;
			bool req3 = true;
			bool req4 = true;
 
			//条件1 八领域的和
			if (a[0 == 255])
			{
				int req1_sum{ 0 };
				req1_sum = a[1] + a[2] + a[3] + a[4] + a[5] + a[6] + a[7] + a[8];
 
				//std::cout << "req1_sum: " << req1_sum << std::endl;
				if (req1_sum >= 510 && req1_sum <= 1530)
				{
					//std::cout << "66666" << std::endl;
					req1 = true;
				}
				else req1 = false;
 
				//条件2  01的模式
				int req2_sum{ 0 };
				//	std::cout << "req2_sum: " << req2_sum << std::endl;
				for (int k = 2; k < 9; k++)
				{
					if (a[k] == 255 && a[k - 1] == 0)
					{
						req2_sum += 1;
					}
				}
				if (req2_sum == 1) req2 = true;
				else req2 = false;
				//条件三
				int req3_sum = a[1] * a[3] * a[5];
				//std::cout << "req3_sum: " << req3_sum << std::endl;
 
				if (req3_sum == 0) req3 = true;
				else req3 = false;
				//条件四
				int req4_sum = a[3] * a[5] * a[7];
				//	std::cout << "req3_sum: " << req3_sum << std::endl;
 
				if (req4_sum == 0) req4 = true;
				else req4 = false;
				if (req1 && req2 && req3 && req4)
				{
					PointSaver1.push_back(Point2l(i, j));
				}
			}
		}
	}
	for (int l=0;l<PointSaver1.size();l++)
	{
		uchar* ptr = Binary.ptr<uchar>(PointSaver1[l].x);
		std::cout << "PointSaver1[l].x: " << PointSaver1[l].x << "PointSaver1[l].y: " << PointSaver1[l].y << std::endl;
		ptr[PointSaver1[l].y] = 0;
	}
 
	//第二轮判断
	std::vector<cv::Point2l> PointSaver2;
	for (int i{ 1 }; i < rows - 1; i++)
	{
		uchar* high = Binary.ptr<uchar>(i - 1);
		uchar* mid = Binary.ptr<uchar>(i);
		uchar* low = Binary.ptr<uchar>(i + 1);
 
		for (int j{ 1 }; j < cols - 1; j++)
		{
			int a1 = mid[j];
			int a2 = high[j];
			int a3 = high[j + 1];
			int a4 = mid[j + 1];
			int a5 = low[j + 1];
			int a6 = low[j];
			int a7 = low[j - 1];
			int a8 = mid[j - 1];
			int a9 = high[j - 1];
			int a[9] = { a1,a2,a3,a4,a5,a6,a7,a8,a9 };
 
			bool req1 = true;
			bool req2 = true;
			bool req3 = true;
			bool req4 = true;
 
			//条件1 八领域的和
			if (a[0 == 255])
			{
				int req1_sum{ 0 };
				req1_sum = a[1] + a[2] + a[3] + a[4] + a[5] + a[6] + a[7] + a[8];
 
				//std::cout << "req1_sum: " << req1_sum << std::endl;
				if (req1_sum >= 510 && req1_sum <= 1530)
				{
					//std::cout << "66666" << std::endl;
					req1 = true;
				}
				else req1 = false;
 
				//条件2  01的模式
				int req2_sum{ 0 };
				//	std::cout << "req2_sum: " << req2_sum << std::endl;
				for (int k = 2; k < 9; k++)
				{
					if (a[k] == 255 && a[k - 1] == 0)
					{
						req2_sum += 1;
					}
				}
				if (req2_sum == 1) req2 = true;
				else req2 = false;
				//条件三
				int req3_sum = a[1] * a[3] * a[5];
				//std::cout << "req3_sum: " << req3_sum << std::endl;
 
				if (req3_sum == 0) req3 = true;
				else req3 = false;
				//条件四
				int req4_sum = a[1] * a[3] * a[7];
				//	std::cout << "req3_sum: " << req3_sum << std::endl;
 
				if (req4_sum == 0) req4 = true;
				else req4 = false;
				if (req1 && req2 && req3 && req4)
				{
					PointSaver2.push_back(Point2l(i, j));
				}
			}
		}
	}
	for (int l = 0; l < PointSaver2.size(); l++)
	{
		uchar* ptr = Binary.ptr<uchar>(PointSaver2[l].x);
		std::cout << "PointSaver[l].x: " << PointSaver2[l].x << "PointSaver[l].y: " << PointSaver2[l].y << std::endl;
		ptr[PointSaver2[l].y] = 0;
	}
 
	cv::imshow("SrcImg", Binary);
	std::cout << "saver_size: " << PointSaver2.size() << std::endl;
	cv::waitKey(0);
}

我的github地址:https://github.com/Dylanin1999/ISP_Algorithm

感谢各位看官。有啥疑惑或看到本人有啥错漏的话,欢迎留言交流!


————————————————

版权声明:本文为CSDN博主「Dylan_young」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。

原文链接:https://blog.csdn.net/weixin_38419133/article/details/98499664



本文出自勇哥的网站《少有人走的路》wwww.skcircle.com,转载请注明出处!讨论可扫码加群:

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

会员中心
搜索
«    2024年4月    »
1234567
891011121314
15161718192021
22232425262728
2930
网站分类
标签列表
最新留言
    热门文章 | 热评文章 | 随机文章
文章归档
友情链接
  • 订阅本站的 RSS 2.0 新闻聚合
  • 扫描加本站机器视觉QQ群,验证答案为:halcon勇哥的机器视觉
  • 点击查阅微信群二维码
  • 扫描加勇哥的非标自动化群,验证答案:C#/C++/VB勇哥的非标自动化群
  • 扫描加站长微信:站长微信:abc496103864
  • 扫描加站长QQ:
  • 扫描赞赏本站:
  • 留言板:

Powered By Z-BlogPHP 1.7.2

Copyright Your skcircle.com Rights Reserved.

鄂ICP备18008319号


站长QQ:496103864 微信:abc496103864