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

效果如图:

图像细化 深度学习方法 图像细化算法流程图_github

图像细化 深度学习方法 图像细化算法流程图_算法原理_02

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

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

一、八领域

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

p9

p2

p3

p8

p1

p4

p7

p6

p5

 

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

二、算法原理

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

          端点                                  孤立点                                内部点                                   内部点

图像细化 深度学习方法 图像细化算法流程图_#include_03

            

图像细化 深度学习方法 图像细化算法流程图_图像细化 深度学习方法_04

             

图像细化 深度学习方法 图像细化算法流程图_#include_05

                

图像细化 深度学习方法 图像细化算法流程图_#include_06

 

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

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

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

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

 

图像细化 深度学习方法 图像细化算法流程图_图像细化 深度学习方法_07

之所以要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);
}

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