OpenCV3历程(4)——寻找直线的十字交叉点

此篇文章转载自:https://www.cnblogs.com/jsxyhelu/p/10295373.html

首先上图:

这里的目标是寻找六条线段的交叉点,我一开始看到这个图片的时候觉得很简单,认为通过霍夫找线段然后求取交叉点就能实现,果不其然,我想太多了,经过对OpenCV里HoughLinesP()函数的参数的各种调节,始终找不到所有的完整的线段,我觉得很大的原因可能是因为图片像素过高,后面我会继续摸索。

下面带来博客大神实现的方法(也存在一定的瑕疵,但效果已经很不错了):

算法实践:

1、这样一个图像,单通道所以无法做HSV分解,必须考虑到光照影响,所以不能采用OSTU,经验告诉我,直接二值化能够得到较好效果。

OpenCV3历程(4)——寻找直线的十字交叉点_第1张图片

 2、得到的结果,可能会有孤立点,需要通过形态学方法去除

OpenCV3历程(4)——寻找直线的十字交叉点_第2张图片

  • 腐蚀:腐蚀是一种消除边界点,使边界向内部收缩的过程。可以用来消除小且无意义的物体。
  • 膨胀:膨胀是将与物体接触的所有背景点合并到该物体中,使边界向外部扩张的过程。可以用来填补物体中的空洞。
  • 闭运算:闭运算用来填充物体内细小空洞、连接邻近物体、平滑其边界的同时并不明显改变其面积。基本上所有小到不能完整容纳结构元素的空隙或间隙,都会被闭运算消除(即连起来)。
  • 开运算:用来背景中的消除小物体、在纤细点处分离物体、平滑较大物体的边界的同时并不明显改变其面积。所有小到不能容纳结构元素的物体都会被移除。

这里我们需要做的事“开运算”,根据观察,我们采用5*5的核:

OpenCV3历程(4)——寻找直线的十字交叉点_第3张图片

得到的结果是奇异点全部消失,但是现有轮廓会有所衰弱

OpenCV3历程(4)——寻找直线的十字交叉点_第4张图片

3、进一步执行细化,精确定位,也会出现细微偏差

OpenCV3历程(4)——寻找直线的十字交叉点_第5张图片

4、那么,这个图像质量,我们如何建立特征提取模型了? 对于这样一个点

OpenCV3历程(4)——寻找直线的十字交叉点_第6张图片

 

4个象限,那么对于我们这些交点来说,肯定是有特征的,比如上图表示,对于这幅图像中的所有的点,可以区分为1-4个象限;就目前这4个象限中来看,每个象限都存在有数据的区域。但是经过编码发现,由于原图中有圆形等其他图像,干扰比较大,所以可以进一步增加约束为“寻找所有该点本身、其左边、右边、上边、下边都为255的点,由于这是一个比较明显的特征,所以我们可以直接编写代码去提取。

全部源码:

#include 
#include "opencv2\core.hpp"
#include "opencv2/shape.hpp"
#include "opencv2\opencv_modules.hpp"
#include 
#include 

using namespace cv;
using namespace std;

#define  DIRECTION_X 0
#define FALSE   0
#define TRUE    1

void thin(const Mat &src, Mat &dst, const int iterations = 100);
void projection2(Mat src, vector& vup, vector& vdown, int direction = DIRECTION_X, int gap = 10);



int main()
{
	Mat src = imread("./image/寻找激光的交叉点.bmp", 0);
	Mat srcClone = src.clone();
	Mat srcClone1 = src.clone();
	Mat srcBoard(src.size(), CV_8U, Scalar::all(0));
	//阈值处理
	threshold(src, src, 100, 255, THRESH_BINARY);
	//形态学去噪音
	Mat element = getStructuringElement(MORPH_RECT, Size(5, 5));
	//开运算,先腐蚀再膨胀,平滑物体轮廓,断开较窄的地方,清除细突
	morphologyEx(src, src, CV_MOP_OPEN, element);
	//二值图像细化处理
	thin(src, src);
	namedWindow("src", WINDOW_NORMAL);
	imshow("src", src);
	imwrite("srccc.jpg", src);
	//遍历图像,这里我在遍历的时候缩了一个像素
	vector vecPoints;
	for (int i = 1; i < src.rows - 1; i++) {
		for (int j = 1; j < src.cols - 1; j++) {
			if (src.at(i, j) == 255) //首先该像素要有数据
			{
				if (src.at(i - 1, j) == 255 && src.at(i + 1, j) == 255 && src.at(i, j - 1) == 255 && src.at(i, j + 1) == 255)
				{
					vecPoints.push_back(Point(j, i));
				}
			}

		}
	}
	//在底图上进行绘制
	cvtColor(srcClone, srcClone, COLOR_GRAY2BGR);
	cvtColor(srcClone1, srcClone1, COLOR_GRAY2BGR);
	//Point a;
	//a.x = vecPoints[0].x;
	//a.y = vecPoints[4].y;
	//circle(srcClone1, a, 10, Scalar(0, 0, 255), -1);
	//namedWindow("srcClone1", WINDOW_NORMAL);
	//imshow("srcClone1", srcClone1);
	//imwrite("srcClone1.jpg", srcClone1);
	for (int i = 0; i < vecPoints.size(); i++)
	{
		circle(srcClone, vecPoints[i], 10, Scalar(0, 0, 255), -1);
		circle(srcBoard, vecPoints[i], 1, Scalar(255), -1);
	}
	namedWindow("srcClone", WINDOW_NORMAL);
	imshow("srcClone", srcClone);
	imwrite("srcClone.jpg", srcClone);

	waitKey();
	return 0;
}

//将 DEPTH_8U型二值图像进行细化  经典的Zhang并行快速细化算法
void thin(const Mat &src, Mat &dst, const int iterations) {
	const int height = src.rows - 1;
	const int width = src.cols - 1;
	//拷贝一个数组给另一个数组
	if (src.data != dst.data)
		src.copyTo(dst);
	int n = 0, i = 0, j = 0;
	Mat tmpImg;
	uchar *pU, *pC, *pD;
	bool isFinished = FALSE;
	for (n = 0; n < iterations; n++) {
		dst.copyTo(tmpImg);
		isFinished = FALSE;   //一次 先行后列扫描 开始
		//扫描过程一 开始
		for (i = 1; i < height; i++) {
			pU = tmpImg.ptr(i - 1);
			pC = tmpImg.ptr(i);
			pD = tmpImg.ptr(i + 1);
			for (int j = 1; j < width; j++) {
				if (pC[j] > 0) {
					int ap = 0;
					int p2 = (pU[j] > 0);
					int p3 = (pU[j + 1] > 0);
					if (p2 == 0 && p3 == 1)
						ap++;
					int p4 = (pC[j + 1] > 0);
					if (p3 == 0 && p4 == 1)
						ap++;
					int p5 = (pD[j + 1] > 0);
					if (p4 == 0 && p5 == 1)
						ap++;
					int p6 = (pD[j] > 0);
					if (p5 == 0 && p6 == 1)
						ap++;
					int p7 = (pD[j - 1] > 0);
					if (p6 == 0 && p7 == 1)
						ap++;
					int p8 = (pC[j - 1] > 0);
					if (p7 == 0 && p8 == 1)
						ap++;
					int p9 = (pU[j - 1] > 0);
					if (p8 == 0 && p9 == 1)
						ap++;
					if (p9 == 0 && p2 == 1)
						ap++;
					if ((p2 + p3 + p4 + p5 + p6 + p7 + p8 + p9) > 1 && (p2 + p3 + p4 + p5 + p6 + p7 + p8 + p9) < 7) {
						if (ap == 1) {
							if ((p2*p4*p6 == 0) && (p4*p6*p8 == 0)) {
								dst.ptr(i)[j] = 0;
								isFinished = TRUE;
							}
						}
					}
				}

			} //扫描过程一 结束
			dst.copyTo(tmpImg);
			//扫描过程二 开始
			for (i = 1; i < height; i++) {
				pU = tmpImg.ptr(i - 1);
				pC = tmpImg.ptr(i);
				pD = tmpImg.ptr(i + 1);
				for (int j = 1; j < width; j++) {
					if (pC[j] > 0) {
						int ap = 0;
						int p2 = (pU[j] > 0);
						int p3 = (pU[j + 1] > 0);
						if (p2 == 0 && p3 == 1)
							ap++;
						int p4 = (pC[j + 1] > 0);
						if (p3 == 0 && p4 == 1)
							ap++;
						int p5 = (pD[j + 1] > 0);
						if (p4 == 0 && p5 == 1)
							ap++;
						int p6 = (pD[j] > 0);
						if (p5 == 0 && p6 == 1)
							ap++;
						int p7 = (pD[j - 1] > 0);
						if (p6 == 0 && p7 == 1)
							ap++;
						int p8 = (pC[j - 1] > 0);
						if (p7 == 0 && p8 == 1)
							ap++;
						int p9 = (pU[j - 1] > 0);
						if (p8 == 0 && p9 == 1)
							ap++;
						if (p9 == 0 && p2 == 1)
							ap++;
						if ((p2 + p3 + p4 + p5 + p6 + p7 + p8 + p9) > 1 && (p2 + p3 + p4 + p5 + p6 + p7 + p8 + p9) < 7) {
							if (ap == 1) {
								if ((p2*p4*p8 == 0) && (p2*p6*p8 == 0)) {
									dst.ptr(i)[j] = 0;
									isFinished = TRUE;
								}
							}
						}
					}
				}
			} //一次 先行后列扫描完成          
			//如果在扫描过程中没有删除点,则提前退出
			if (isFinished == FALSE)
				break;
		}
	}
}

得到以下结果:

OpenCV3历程(4)——寻找直线的十字交叉点_第7张图片

可以看到,并没有得到全部的交叉点,我们需要对得到有限的点进行补全和排序,具体情况还是要具体分析,这里只做到这了!

你可能感兴趣的:(OpenCV3历程)