提取接近竖直物体(粗定位)

5 篇文章 0 订阅

由于项目的需要提取图像之中的一个接近于竖直的物体,一般的方法是进行图像分割,分割方式使用什么OTSU方式以及hsv方法等等。但是项目中使用的相机是黑白相机,会受到一定的限制。因此想到的是使用线条提取方式。线条提取方式之中最好的方法是使用canny算法,但是这里不能够将接近竖直特征进行提取,因此,此处使用了Prewitt算子进行提取,但是只用这个算法,轮廓提取不出来,就结合了一下canny算子。下面是我的思路,感觉实现过程比较麻烦,但是居然实现了[苦笑]!!!!

本次测试的案例是使用校门口的一个图片,图中存在很多的干扰,如下图所示

#include <opencv2/opencv.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <iostream>
#include <opencv2/core.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
using namespace cv;
using namespace std;


void getPrewitt_oper(cv::Mat& getPrewitt_horizontal, cv::Mat& getPrewitt_vertical, cv::Mat& getPrewitt_Diagonal1, cv::Mat& getPrewitt_Diagonal2) {
	//水平方向
	getPrewitt_horizontal = (cv::Mat_<float>(3, 3) << -1, -1, -1, 0, 0, 0, 1, 1, 1);
	//垂直方向
	getPrewitt_vertical = (cv::Mat_<float>(3, 3) << -1, 0, 1, -1, 0, 1, -1, 0, 1);
	//对角135°
	getPrewitt_Diagonal1 = (cv::Mat_<float>(3, 3) << 0, 1, 1, -1, 0, 1, -1, -1, 0);
	//对角45°
	getPrewitt_Diagonal2 = (cv::Mat_<float>(3, 3) << -1, -1, 0, -1, 0, 1, 0, 1, 1);

	//逆时针反转180°得到卷积核
	cv::flip(getPrewitt_horizontal, getPrewitt_horizontal, -1);
	cv::flip(getPrewitt_vertical, getPrewitt_vertical, -1);
	cv::flip(getPrewitt_Diagonal1, getPrewitt_Diagonal1, -1);
	cv::flip(getPrewitt_Diagonal2, getPrewitt_Diagonal2, -1);
}

void edge_Prewitt(cv::Mat& src, cv::Mat& dst1, cv::Mat& dst2, cv::Mat& dst3, cv::Mat& dst4, cv::Mat& dst, int ddepth, double delta = 0, int borderType = cv::BORDER_DEFAULT) {
	//获取Prewitt算子
	cv::Mat getPrewitt_horizontal;
	cv::Mat getPrewitt_vertical;
	cv::Mat getPrewitt_Diagonal1;
	cv::Mat getPrewitt_Diagonal2;
	getPrewitt_oper(getPrewitt_horizontal, getPrewitt_vertical, getPrewitt_Diagonal1, getPrewitt_Diagonal2);

	//卷积得到水平方向边缘
	cv::filter2D(src, dst1, ddepth, getPrewitt_horizontal, cv::Point(-1, -1), delta, borderType);

	//卷积得到4垂直方向边缘
	cv::filter2D(src, dst2, ddepth, getPrewitt_vertical, cv::Point(-1, -1), delta, borderType);

	//卷积得到45°方向边缘
	cv::filter2D(src, dst3, ddepth, getPrewitt_Diagonal1, cv::Point(-1, -1), delta, borderType);

	//卷积得到135°方向边缘
	cv::filter2D(src, dst4, ddepth, getPrewitt_Diagonal2, cv::Point(-1, -1), delta, borderType);

	//边缘强度(近似)
	cv::convertScaleAbs(dst1, dst1); //求绝对值并转为无符号8位图
	cv::convertScaleAbs(dst2, dst2);

	cv::convertScaleAbs(dst3, dst3); //求绝对值并转为无符号8位图
	cv::convertScaleAbs(dst4, dst4);
	dst = dst1 + dst2;

}

//数组从大到小排序
void reserve(int x[], int n) {
	int i, j, temp;
	for (i = 0; i < n - 1; i++) {     //一共n个元素,则需要比较n-1次
		for (j = 0; j < n - 1 - i; j++) {     //每一个元素需要比较的次数
			if (x[i] < x[i + j + 1]) {
				temp = x[i];
				x[i] = x[i + j + 1];
				x[i + j + 1] = temp;
			}
		}
	}
}



int main()
{
	cv::Mat src = cv::imread("楼.jpg");
	if (src.empty()) {
		return -1;
	}
	cout << "??" << endl;
	if (src.channels() > 1) cv::cvtColor(src, src, CV_RGB2GRAY);
	cv::Mat dst, dst1, dst2, dst3, dst4, dst5;

	Mat src1 = cv::imread("楼.jpg");
	Mat src2 = cv::imread("楼.jpg");

	//medianBlur(src, src, 5);  //均值滤波
	GaussianBlur(src, src, Size(5, 5), 0); //高斯滤波
	cout << "??" << endl;

	//注意:要采用CV_32F,因为有些地方卷积后为负数,若用8位无符号,则会导致这些地方为0
	edge_Prewitt(src, dst1, dst2, dst3, dst4, dst, CV_32F);

	cv::namedWindow("垂直边缘", CV_WINDOW_NORMAL);
	imshow("垂直边缘", dst2);
	cout << "??" << endl;
	
	
	
	//获取结构
	cv::Mat element1 = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(3, 3));

	Mat out1;
	//进行形态学开运算操作  
	morphologyEx(dst2, out1, MORPH_OPEN, element1);//形态学开运算
	cv::namedWindow("xingtai", CV_WINDOW_NORMAL);
	imshow("xingtai", out1);

	


	//第二次进行形态学操作
	edge_Prewitt(dst2, dst1, out1, dst3, dst4, dst, CV_32F);
	cv::namedWindow("垂直边缘1", CV_WINDOW_NORMAL);
	imshow("垂直边缘1", out1);
	cout << "??" << endl;

	morphologyEx(out1, out1, MORPH_OPEN, element1);//形态学开运算
	cv::namedWindow("xingtai1", CV_WINDOW_NORMAL);
	imshow("xingtai1", out1);


	//获取结构
	cv::Mat element2 = cv::getStructuringElement(cv::MORPH_ELLIPSE, cv::Size(10, 10));

	Mat out2;
	//进行形态学闭运算操作  
	morphologyEx(out1, out2, MORPH_CLOSE, element2);//形态学开运算
	cv::namedWindow("xingtai2", CV_WINDOW_NORMAL);
	imshow("xingtai2", out2);
	imwrite("xingtai2.jpg", out2);

	/*
	//膨胀运算,将细小缝隙填补上,非必要
	Mat kernel = getStructuringElement(0, Size(3, 3));
	dilate(out2, dst2, kernel);
	cv::namedWindow("膨胀", CV_WINDOW_NORMAL);
	imshow("膨胀", dst2);
	*/
	
	cv::threshold(out2, dst2, 5, 255, cv::THRESH_BINARY);
	cv::namedWindow("二值化", CV_WINDOW_NORMAL);
	imshow("二值化", dst2);

	cv::threshold(dst2, dst2, 5, 255, cv::THRESH_BINARY_INV);
	cv::namedWindow("反二值化", CV_WINDOW_NORMAL);
	imshow("反二值化", dst2);

	
	//进行形态学闭运算操作  
	morphologyEx(dst2, out2, MORPH_CLOSE, element2);//形态学开运算
	cv::namedWindow("xingtai3", CV_WINDOW_NORMAL);
	imshow("xingtai3", out2);
	imwrite("xingtai3.jpg", out2);

	/*
	cv::threshold(dst2, dst2, 5, 255, cv::THRESH_BINARY);
	cv::namedWindow("反二值化", CV_WINDOW_NORMAL);
	imshow("反二值化", dst2);
	imwrite("反二值化.jpg", dst2);
	*/

	/*
	//膨胀运算,将细小缝隙填补上,非必要
    Mat kernel = getStructuringElement(0, Size(5, 5));
	dilate(out2, out2, kernel);
	cv::namedWindow("膨胀1", CV_WINDOW_NORMAL);
	imshow("膨胀1", out2);*/

	Canny(out2, dst2, 5, 10);
	cv::namedWindow("Canny", CV_WINDOW_NORMAL);
	imshow("Canny", dst2);
	imwrite("Canny.jpg", dst2);
	

	vector<Vec4i> lines;
	HoughLinesP(dst2, lines, 1, CV_PI / 180, 50, 200, 30);
	int Length[100] = {0};//存放直线长度
	for (size_t i = 0; i < lines.size(); i++)
	{
		Vec4i I = lines[i];
		double x1 = I[0];
		double y1 = I[1];
		double x2 = I[2];
		double y2 = I[3];

		//筛选满足条件的点
		if (abs(x1 - x2) + abs(y1 - y2) > 50)
		{
			Length[i] = sqrt( (x1 - x2)*(x1 - x2) + (y1 - y2) * (y1 - y2));
			//将满足条件的点画出
			line(src1, Point2d(x1, y1), Point2d(x2, y2), Scalar(0, 255, 255), 2);

			cout << " " << "(" << x1 << "," << y1 << ")" << " " << "(" << x2 << "," << y2 << ")" << endl;
			//line(canny, Point2d(x1, y1), Point2d(x2, y2), Scalar(0, 255, 255), 2);
		}
	}

	Mat imgShow;
	imgShow = src1;
	resize(imgShow, imgShow, Size(imgShow.cols / 4, imgShow.rows / 4));
	imshow("imgShow", imgShow);
	imwrite("shuchu.png", src1);


	reserve(Length, 100);
	for (int i = 0; i < 100; i++) {
		cout << "长度"<<Length[i] << endl;   //输出排序后的数组元素
	}

	for (size_t i = 0; i < lines.size(); i++)
	{
		Vec4i I = lines[i];
		double x1 = I[0];
		double y1 = I[1];
		double x2 = I[2];
		double y2 = I[3];
		cout << "sdjk" << endl;
		cout << int(sqrt((x1 - x2)*(x1 - x2) + (y1 - y2) * (y1 - y2))) << endl;
		//筛选满足条件的点
		if ((int(sqrt((x1 - x2)*(x1 - x2) + (y1 - y2) * (y1 - y2))) == Length[0] ) || (int(sqrt((x1 - x2)*(x1 - x2) + (y1 - y2) * (y1 - y2))) == Length[1]))
		{
			
			//将满足条件的点画出
			line(src2, Point2d(x1, y1), Point2d(x2, y2), Scalar(0, 255, 255), 2);

			cout << "djfkljsa " << "(" << x1 << "," << y1 << ")" << " " << "(" << x2 << "," << y2 << ")" << endl;
			//line(canny, Point2d(x1, y1), Point2d(x2, y2), Scalar(0, 255, 255), 2);
		}
	}
	
	imgShow = src2;
	resize(imgShow, imgShow, Size(imgShow.cols / 4, imgShow.rows / 4));
	imshow("imgShow2", imgShow);
	imwrite("shuchu2.png", src2);





	waitKey(0);
	return 0;
}

调试过程:本次在进行调试过程之中进行了两次垂直检测迭代,进一步去排除水平线的干扰.使用形态学操作去除图片之中的空洞等等.

第一次进行垂直检测,注意这个地方只能够用特定的算子进行垂直检测,别的算子没有这个效果. 

为了减少图片之中白色空洞的干扰,使用开操作.

重复上述操作,进一步排除水平线的干扰.

接下来是进行闭操作,将图中的白色线条尽可能连在一起,上图之中的楼左侧的线有一些断开了.

闭操作的缺陷是会产生小白点点.如下二值化过程

再进行一次反二值化,因为我不会用别的算子结合霍夫直线检测检测出来直线,只能转回去进行操作. 

形态学操作,去除白点

 canny一下检测出来轮廓

显示全部直线

 直线提取,我的方式是提取最长的两段直线。

 在上述操作完成之后,得到了物体的粗定位直线。

但是上面的算法还是存在相应的问题,换了一个别的图像可能就检测的不准。发现问题就是出在了二值化的过程。

为了修正上方的算法的失败,使用提取外部轮廓的方式进行求取,将代码改了改。
 

#include <opencv2/opencv.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <iostream>
#include <opencv2/core.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
using namespace cv;
using namespace std;


void getPrewitt_oper(cv::Mat& getPrewitt_horizontal, cv::Mat& getPrewitt_vertical, cv::Mat& getPrewitt_Diagonal1, cv::Mat& getPrewitt_Diagonal2) {
	//水平方向
	getPrewitt_horizontal = (cv::Mat_<float>(3, 3) << -1, -1, -1, 0, 0, 0, 1, 1, 1);
	//垂直方向
	getPrewitt_vertical = (cv::Mat_<float>(3, 3) << -1, 0, 1, -1, 0, 1, -1, 0, 1);
	//对角135°
	getPrewitt_Diagonal1 = (cv::Mat_<float>(3, 3) << 0, 1, 1, -1, 0, 1, -1, -1, 0);
	//对角45°
	getPrewitt_Diagonal2 = (cv::Mat_<float>(3, 3) << -1, -1, 0, -1, 0, 1, 0, 1, 1);

	//逆时针反转180°得到卷积核
	cv::flip(getPrewitt_horizontal, getPrewitt_horizontal, -1);
	cv::flip(getPrewitt_vertical, getPrewitt_vertical, -1);
	cv::flip(getPrewitt_Diagonal1, getPrewitt_Diagonal1, -1);
	cv::flip(getPrewitt_Diagonal2, getPrewitt_Diagonal2, -1);
}

void edge_Prewitt(cv::Mat& src, cv::Mat& dst1, cv::Mat& dst2, cv::Mat& dst3, cv::Mat& dst4, cv::Mat& dst, int ddepth, double delta = 0, int borderType = cv::BORDER_DEFAULT) {
	//获取Prewitt算子
	cv::Mat getPrewitt_horizontal;
	cv::Mat getPrewitt_vertical;
	cv::Mat getPrewitt_Diagonal1;
	cv::Mat getPrewitt_Diagonal2;
	getPrewitt_oper(getPrewitt_horizontal, getPrewitt_vertical, getPrewitt_Diagonal1, getPrewitt_Diagonal2);

	//卷积得到水平方向边缘
	cv::filter2D(src, dst1, ddepth, getPrewitt_horizontal, cv::Point(-1, -1), delta, borderType);

	//卷积得到4垂直方向边缘
	cv::filter2D(src, dst2, ddepth, getPrewitt_vertical, cv::Point(-1, -1), delta, borderType);

	//卷积得到45°方向边缘
	cv::filter2D(src, dst3, ddepth, getPrewitt_Diagonal1, cv::Point(-1, -1), delta, borderType);

	//卷积得到135°方向边缘
	cv::filter2D(src, dst4, ddepth, getPrewitt_Diagonal2, cv::Point(-1, -1), delta, borderType);

	//边缘强度(近似)
	cv::convertScaleAbs(dst1, dst1); //求绝对值并转为无符号8位图
	cv::convertScaleAbs(dst2, dst2);

	cv::convertScaleAbs(dst3, dst3); //求绝对值并转为无符号8位图
	cv::convertScaleAbs(dst4, dst4);
	dst = dst1 + dst2;

}

//数组从大到小排序
void reserve(int x[], int n) {
	int i, j, temp;
	for (i = 0; i < n - 1; i++) {     //一共n个元素,则需要比较n-1次
		for (j = 0; j < n - 1 - i; j++) {     //每一个元素需要比较的次数
			if (x[i] < x[i + j + 1]) {
				temp = x[i];
				x[i] = x[i + j + 1];
				x[i + j + 1] = temp;
			}
		}
	}
}



int main()
{
	cv::Mat src = cv::imread("楼.jpg");
	if (src.empty()) {
		return -1;
	}
	cout << "??" << endl;
	if (src.channels() > 1) cv::cvtColor(src, src, CV_RGB2GRAY);
	cv::Mat dst, dst1, dst2, dst3, dst4, dst5;

	Mat src1 = cv::imread("楼.jpg");
	Mat src2 = cv::imread("楼.jpg");

	//medianBlur(src, src, 5);  //均值滤波
	GaussianBlur(src, src, Size(5, 5), 0); //高斯滤波
	cout << "??" << endl;

	//注意:要采用CV_32F,因为有些地方卷积后为负数,若用8位无符号,则会导致这些地方为0
	edge_Prewitt(src, dst1, dst2, dst3, dst4, dst, CV_32F);

	cv::namedWindow("垂直边缘", CV_WINDOW_NORMAL);
	imshow("垂直边缘", dst2);
	cout << "??" << endl;

	

	/*
	Mat shdjk;
	cv::threshold(dst2, shdjk, 25, 255, cv::THRESH_BINARY);
	cv::namedWindow("二值化1212", CV_WINDOW_NORMAL);
	imshow("二值化1212", shdjk);
	*/

	//获取结构
	cv::Mat element1 = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(3, 3));

	Mat out1;
	//进行形态学开运算操作  
	morphologyEx(dst2, out1, MORPH_OPEN, element1);//形态学开运算
	cv::namedWindow("xingtai", CV_WINDOW_NORMAL);
	imshow("xingtai", out1);


	Mat out2;
	//第二次进行形态学操作
	edge_Prewitt(out1, dst1, out1, dst3, dst4, dst, CV_32F);
	cv::namedWindow("垂直边缘1", CV_WINDOW_NORMAL);
	imshow("垂直边缘1", out1);
	cout << "??" << endl;


	/*
	morphologyEx(out1, out1, MORPH_OPEN, element1);//形态学开运算
	cv::namedWindow("xingtai1", CV_WINDOW_NORMAL);
	imshow("xingtai1", out1);

	//获取结构
	cv::Mat element2 = cv::getStructuringElement(cv::MORPH_ELLIPSE, cv::Size(10, 10));

	
	//进行形态学闭运算操作  
	morphologyEx(out1, out2, MORPH_CLOSE, element2);//形态学闭合运算
	cv::namedWindow("xingtai2", CV_WINDOW_NORMAL);
	imshow("xingtai2", out2);
	imwrite("xingtai2.jpg", out2);
	
	waitKey(0);
	*/
	std::vector<std::vector<cv::Point>> contours;
	std::vector<cv::Vec4i> hierarchy;
	findContours(out1, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);
	double maxArea = 0;
	int index = 0;
	vector<cv::Point> maxContour;
	for (size_t i = 0; i < contours.size(); i++)
	{
		double area = cv::contourArea(contours[i]);
		if (area > maxArea)
		{
			maxArea = area;
			maxContour = contours[i];
			index = i;
		}
	}
	drawContours(src1, contours, index, Scalar(255));    // 参数
	cv::namedWindow("test", CV_WINDOW_NORMAL);
	imshow("test", src1);
	waitKey(0);
	/*
	Mat shdjk;
	cv::threshold(out1, shdjk, 10, 255, cv::THRESH_BINARY);
	cv::namedWindow("二值化1212", CV_WINDOW_NORMAL);
	imshow("二值化1212", shdjk);

	std::vector<std::vector<cv::Point>> contours;
	std::vector<cv::Vec4i> hierarchy;
	cv::findContours(shdjk, contours, hierarchy, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_NONE);  //只找最外层轮廓


	std::vector<std::vector<cv::Point>> approxCurves(contours.size());
	for (int i = 0; i < contours.size(); ++i) {  //绘制逼近后的轮廓
		double epsilon = 0.1 * cv::arcLength(contours[i], true);
		cv::approxPolyDP(contours[i], approxCurves[i], epsilon, true);

		cv::drawContours(src1, approxCurves, i, cv::Scalar(0, 255, 0), 2);
	}
	cv::namedWindow("success", CV_WINDOW_NORMAL);
	imshow("success", src1);

	cv::waitKey();

	*/

	

	

	//
	/*
	Mat dhfjua;
	cv::threshold(out2, dhfjua, 15, 255, cv::THRESH_BINARY);
	cv::namedWindow("二值化000", CV_WINDOW_NORMAL);
	imshow("二值化000", dhfjua);
	*/
	/*
	//膨胀运算,将细小缝隙填补上,非必要
	Mat kernel = getStructuringElement(0, Size(3, 3));
	dilate(out2, dst2, kernel);
	cv::namedWindow("膨胀", CV_WINDOW_NORMAL);
	imshow("膨胀", dst2);
	*/
	/*0
	cv::threshold(out2, dst2, 5, 255, cv::THRESH_BINARY);
	cv::namedWindow("二值化", CV_WINDOW_NORMAL);
	imshow("二值化", dst2);

	cv::threshold(dst2, dst2, 5, 255, cv::THRESH_BINARY_INV);
	cv::namedWindow("反二值化", CV_WINDOW_NORMAL);
	imshow("反二值化", dst2);
	*/
	/*
	Mat out3;
	//进行形态学闭运算操作  
	morphologyEx(dst2, out3, MORPH_CLOSE, element2);//形态学开运算
	cv::namedWindow("xingtai3", CV_WINDOW_NORMAL);
	imshow("xingtai3", out3);
	imwrite("xingtai3.jpg", out3);
	*/

	waitKey(0);
	vector<Vec4i> lines;
	HoughLinesP(src1, lines, 1, CV_PI / 180, 100, 400, 30);
	int Length[1000] = { 0 };//存放直线长度
	for (size_t i = 0; i < lines.size(); i++)
	{
		Vec4i I = lines[i];
		double x1 = I[0];
		double y1 = I[1];
		double x2 = I[2];
		double y2 = I[3];

		//筛选满足条件的点
		if (abs(x1 - x2) + abs(y1 - y2) > 50)
		{
			Length[i] = sqrt((x1 - x2)*(x1 - x2) + (y1 - y2) * (y1 - y2));
			//将满足条件的点画出
			line(src1, Point2d(x1, y1), Point2d(x2, y2), Scalar(0, 255, 255), 2);

			cout << " " << "(" << x1 << "," << y1 << ")" << " " << "(" << x2 << "," << y2 << ")" << endl;
			//line(canny, Point2d(x1, y1), Point2d(x2, y2), Scalar(0, 255, 255), 2);
		}
	}

	

	Mat imgShow;
	imgShow = src1;
	resize(imgShow, imgShow, Size(imgShow.cols / 4, imgShow.rows / 4));
	imshow("imgShow", imgShow);
	imwrite("shuchu.png", src1);


	reserve(Length, 1000);
	for (int i = 0; i < 1000; i++) {
		cout << "长度" << Length[i] << endl;   //输出排序后的数组元素
	}

	for (size_t i = 0; i < lines.size(); i++)
	{
		Vec4i I = lines[i];
		double x1 = I[0];
		double y1 = I[1];
		double x2 = I[2];
		double y2 = I[3];
		cout << "sdjk" << endl;
		cout << int(sqrt((x1 - x2)*(x1 - x2) + (y1 - y2) * (y1 - y2))) << endl;
		//筛选满足条件的点
		if ((int(sqrt((x1 - x2)*(x1 - x2) + (y1 - y2) * (y1 - y2))) == Length[0]) || (int(sqrt((x1 - x2)*(x1 - x2) + (y1 - y2) * (y1 - y2))) == Length[1]))
		{

			//将满足条件的点画出
			line(src2, Point2d(x1, y1), Point2d(x2, y2), Scalar(0, 255, 255), 2);

			cout << "djfkljsa " << "(" << x1 << "," << y1 << ")" << " " << "(" << x2 << "," << y2 << ")" << endl;
			//line(canny, Point2d(x1, y1), Point2d(x2, y2), Scalar(0, 255, 255), 2);
		}
	}

	imgShow = src2;
	resize(imgShow, imgShow, Size(imgShow.cols / 4, imgShow.rows / 4));
	imshow("imgShow2", imgShow);
	imwrite("shuchu2.png", src2);





	waitKey(0);
	return 0;
}

效果还是不好,问题就是出在了相应的一个二值化的过程,因此,想到使用区域增长算法改进

#include <iostream>
#include <string>
#include <list>
#include <vector>
#include <map>
#include <stack>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>


using namespace std;
using namespace cv;


//------------------------------【两步法新改进版】----------------------------------------------
// 对二值图像进行连通区域标记,从1开始标号
void  Two_PassNew(const Mat &bwImg, Mat &labImg)
{
	assert(bwImg.type() == CV_8UC1);
	labImg.create(bwImg.size(), CV_32SC1);   //bwImg.convertTo( labImg, CV_32SC1 );
	labImg = Scalar(0);
	labImg.setTo(Scalar(1), bwImg);
	assert(labImg.isContinuous());
	const int Rows = bwImg.rows - 1, Cols = bwImg.cols - 1;
	int label = 1;
	vector<int> labelSet;
	labelSet.push_back(0);
	labelSet.push_back(1);
	//the first pass
	int *data_prev = (int*)labImg.data;   //0-th row : int* data_prev = labImg.ptr<int>(i-1);
	int *data_cur = (int*)(labImg.data + labImg.step); //1-st row : int* data_cur = labImg.ptr<int>(i);
	for (int i = 1; i < Rows; i++)
	{
		data_cur++;
		data_prev++;
		for (int j = 1; j < Cols; j++, data_cur++, data_prev++)
		{
			if (*data_cur != 1)
				continue;
			int left = *(data_cur - 1);
			int up = *data_prev;
			int neighborLabels[2];
			int cnt = 0;
			if (left > 1)
				neighborLabels[cnt++] = left;
			if (up > 1)
				neighborLabels[cnt++] = up;
			if (!cnt)
			{
				labelSet.push_back(++label);
				labelSet[label] = label;
				*data_cur = label;
				continue;
			}
			int smallestLabel = neighborLabels[0];
			if (cnt == 2 && neighborLabels[1] < smallestLabel)
				smallestLabel = neighborLabels[1];
			*data_cur = smallestLabel;
			// 保存最小等价表
			for (int k = 0; k < cnt; k++)
			{
				int tempLabel = neighborLabels[k];
				int& oldSmallestLabel = labelSet[tempLabel];  //这里的&不是取地址符号,而是引用符号
				if (oldSmallestLabel > smallestLabel)
				{
					labelSet[oldSmallestLabel] = smallestLabel;
					oldSmallestLabel = smallestLabel;
				}
				else if (oldSmallestLabel < smallestLabel)
					labelSet[smallestLabel] = oldSmallestLabel;
			}
		}
		data_cur++;
		data_prev++;
	}
	//更新等价队列表,将最小标号给重复区域
	for (size_t i = 2; i < labelSet.size(); i++)
	{
		int curLabel = labelSet[i];
		int prelabel = labelSet[curLabel];
		while (prelabel != curLabel)
		{
			curLabel = prelabel;
			prelabel = labelSet[prelabel];
		}
		labelSet[i] = curLabel;
	}
	//second pass
	data_cur = (int*)labImg.data;
	for (int i = 0; i < Rows; i++)
	{
		for (int j = 0; j < bwImg.cols - 1; j++, data_cur++)
			*data_cur = labelSet[*data_cur];
		data_cur++;
	}
}

//-------------------------------【老版两步法】-------------------------------------------
void Two_PassOld(const cv::Mat& _binImg, cv::Mat& _lableImg)
{
	//connected component analysis (4-component)
	//use two-pass algorithm
	//1. first pass: label each foreground pixel with a label
	//2. second pass: visit each labeled pixel and merge neighbor label
	//
	//foreground pixel: _binImg(x,y) = 1
	//background pixel: _binImg(x,y) = 0

	if (_binImg.empty() || _binImg.type() != CV_8UC1)
	{
		return;
	}

	// 1. first pass
	_lableImg.release();
	_binImg.convertTo(_lableImg, CV_32SC1);

	int label = 1;  // start by 2
	std::vector<int> labelSet;
	labelSet.push_back(0);   //background: 0
	labelSet.push_back(1);   //foreground: 1

	int rows = _binImg.rows - 1;
	int cols = _binImg.cols - 1;
	for (int i = 1; i < rows; i++)
	{
		int* data_preRow = _lableImg.ptr<int>(i - 1);
		int* data_curRow = _lableImg.ptr<int>(i);
		for (int j = 1; j < cols; j++)
		{
			if (data_curRow[j] == 1)
			{
				std::vector<int> neighborLabels;
				neighborLabels.reserve(2); //reserve(n)  预分配n个元素的存储空间
				int leftPixel = data_curRow[j - 1];
				int upPixel = data_preRow[j];
				if (leftPixel > 1)
				{
					neighborLabels.push_back(leftPixel);
				}
				if (upPixel > 1)
				{
					neighborLabels.push_back(upPixel);
				}
				if (neighborLabels.empty())
				{
					labelSet.push_back(++label);   //assign to a new label
					data_curRow[j] = label;
					labelSet[label] = label;
				}
				else
				{
					std::sort(neighborLabels.begin(), neighborLabels.end());
					int smallestLabel = neighborLabels[0];
					data_curRow[j] = smallestLabel;

					//save equivalence
					for (size_t k = 1; k < neighborLabels.size(); k++)
					{
						int tempLabel = neighborLabels[k];
						int& oldSmallestLabel = labelSet[tempLabel];
						if (oldSmallestLabel > smallestLabel)
						{
							labelSet[oldSmallestLabel] = smallestLabel;
							oldSmallestLabel = smallestLabel;
						}
						else if (oldSmallestLabel < smallestLabel)
						{
							labelSet[smallestLabel] = oldSmallestLabel;
						}
					}
				}

			}
		}
	}
	//update equivalent labels
	//assigned with the smallest label in each equivalent label set
	for (size_t i = 2; i < labelSet.size(); i++)
	{
		int curLabel = labelSet[i];
		int prelabel = labelSet[curLabel];
		while (prelabel != curLabel)
		{
			curLabel = prelabel;
			prelabel = labelSet[prelabel];
		}
		labelSet[i] = curLabel;
	}

	//2. second pass
	for (int i = 0; i < rows; i++)
	{
		int *data = _lableImg.ptr<int>(i);
		for (int j = 0; j < cols; j++)
		{
			int& pixelLabel = data[j];
			pixelLabel = labelSet[pixelLabel];
		}
	}
}


//---------------------------------【种子填充法老版】-------------------------------
void SeedFillOld(const cv::Mat& binImg, cv::Mat& lableImg)   //种子填充法
{
	// 4邻接方法


	if (binImg.empty() ||
		binImg.type() != CV_8UC1)
	{
		return;
	}

	lableImg.release();
	binImg.convertTo(lableImg, CV_32SC1);

	int label = 1;

	int rows = binImg.rows - 1;
	int cols = binImg.cols - 1;
	for (int i = 1; i < rows - 1; i++)
	{
		int* data = lableImg.ptr<int>(i);
		for (int j = 1; j < cols - 1; j++)
		{
			if (data[j] == 1)
			{
				std::stack<std::pair<int, int>> neighborPixels;
				neighborPixels.push(std::pair<int, int>(i, j));     // 像素位置: <i,j>
				++label;  // 没有重复的团,开始新的标签
				while (!neighborPixels.empty())
				{
					std::pair<int, int> curPixel = neighborPixels.top(); //如果与上一行中一个团有重合区域,则将上一行的那个团的标号赋给它
					int curX = curPixel.first;
					int curY = curPixel.second;
					lableImg.at<int>(curX, curY) = label;

					neighborPixels.pop();

					if (lableImg.at<int>(curX, curY - 1) == 1)
					{//左边
						neighborPixels.push(std::pair<int, int>(curX, curY - 1));
					}
					if (lableImg.at<int>(curX, curY + 1) == 1)
					{// 右边
						neighborPixels.push(std::pair<int, int>(curX, curY + 1));
					}
					if (lableImg.at<int>(curX - 1, curY) == 1)
					{// 上边
						neighborPixels.push(std::pair<int, int>(curX - 1, curY));
					}
					if (lableImg.at<int>(curX + 1, curY) == 1)
					{// 下边
						neighborPixels.push(std::pair<int, int>(curX + 1, curY));
					}
				}
			}
		}
	}

}




//-------------------------------------------【种子填充法新版】---------------------------
void SeedFillNew(const cv::Mat& _binImg, cv::Mat& _lableImg)
{
	// connected component analysis(4-component)
	// use seed filling algorithm
	// 1. begin with a forgeground pixel and push its forground neighbors into a stack;
	// 2. pop the pop pixel on the stack and label it with the same label until the stack is empty
	// 
	//  forground pixel: _binImg(x,y)=1
	//  background pixel: _binImg(x,y) = 0


	if (_binImg.empty() ||
		_binImg.type() != CV_8UC1)
	{
		return;
	}

	_lableImg.release();
	_binImg.convertTo(_lableImg, CV_32SC1);

	int label = 0; //start by 1

	int rows = _binImg.rows;
	int cols = _binImg.cols;

	Mat mask(rows, cols, CV_8UC1);
	mask.setTo(0);
	int *lableptr;
	for (int i = 0; i < rows; i++)
	{
		int* data = _lableImg.ptr<int>(i);
		uchar *masKptr = mask.ptr<uchar>(i);
		for (int j = 0; j < cols; j++)
		{
			if (data[j] == 255 && mask.at<uchar>(i, j) != 1)
			{
				mask.at<uchar>(i, j) = 1;
				std::stack<std::pair<int, int>> neighborPixels;
				neighborPixels.push(std::pair<int, int>(i, j)); // pixel position: <i,j>
				++label; //begin with a new label
				while (!neighborPixels.empty())
				{
					//get the top pixel on the stack and label it with the same label
					std::pair<int, int> curPixel = neighborPixels.top();
					int curY = curPixel.first;
					int curX = curPixel.second;
					_lableImg.at<int>(curY, curX) = label;

					//pop the top pixel
					neighborPixels.pop();

					//push the 4-neighbors(foreground pixels)

					if (curX - 1 >= 0)
					{
						if (_lableImg.at<int>(curY, curX - 1) == 255 && mask.at<uchar>(curY, curX - 1) != 1) //leftpixel
						{
							neighborPixels.push(std::pair<int, int>(curY, curX - 1));
							mask.at<uchar>(curY, curX - 1) = 1;
						}
					}
					if (curX + 1 <= cols - 1)
					{
						if (_lableImg.at<int>(curY, curX + 1) == 255 && mask.at<uchar>(curY, curX + 1) != 1)
							// right pixel
						{
							neighborPixels.push(std::pair<int, int>(curY, curX + 1));
							mask.at<uchar>(curY, curX + 1) = 1;
						}
					}
					if (curY - 1 >= 0)
					{
						if (_lableImg.at<int>(curY - 1, curX) == 255 && mask.at<uchar>(curY - 1, curX) != 1)
							// up pixel
						{
							neighborPixels.push(std::pair<int, int>(curY - 1, curX));
							mask.at<uchar>(curY - 1, curX) = 1;
						}
					}
					if (curY + 1 <= rows - 1)
					{
						if (_lableImg.at<int>(curY + 1, curX) == 255 && mask.at<uchar>(curY + 1, curX) != 1)
							//down pixel
						{
							neighborPixels.push(std::pair<int, int>(curY + 1, curX));
							mask.at<uchar>(curY + 1, curX) = 1;
						}
					}
				}
			}
		}
	}
}


//---------------------------------【颜色标记程序】-----------------------------------
//彩色显示
cv::Scalar GetRandomColor()
{
	uchar r = 255 * (rand() / (1.0 + RAND_MAX));
	uchar g = 255 * (rand() / (1.0 + RAND_MAX));
	uchar b = 255 * (rand() / (1.0 + RAND_MAX));
	return cv::Scalar(b, g, r);
}


void LabelColor(const cv::Mat& labelImg, cv::Mat& colorLabelImg)
{
	int num = 0;
	if (labelImg.empty() ||
		labelImg.type() != CV_32SC1)
	{
		return;
	}

	std::map<int, cv::Scalar> colors;

	int rows = labelImg.rows;
	int cols = labelImg.cols;

	colorLabelImg.release();
	colorLabelImg.create(rows, cols, CV_8UC3);
	colorLabelImg = cv::Scalar::all(0);

	for (int i = 0; i < rows; i++)
	{
		const int* data_src = (int*)labelImg.ptr<int>(i);
		uchar* data_dst = colorLabelImg.ptr<uchar>(i);
		for (int j = 0; j < cols; j++)
		{
			int pixelValue = data_src[j];
			if (pixelValue > 1)
			{
				if (colors.count(pixelValue) <= 0)
				{
					colors[pixelValue] = GetRandomColor();
					num++;
				}

				cv::Scalar color = colors[pixelValue];
				*data_dst++ = color[0];
				*data_dst++ = color[1];
				*data_dst++ = color[2];
			}
			else
			{
				data_dst++;
				data_dst++;
				data_dst++;
			}
		}
	}
	printf("color num : %d \n", num);
}

//------------------------------------------【测试主程序】-------------------------------------
int main()
{

	cv::Mat binImage = cv::imread("sda.jpg", 0);
	//cv::threshold(binImage, binImage, 50, 1, CV_THRESH_BINARY);
	cv::Mat labelImg;
	double time;
	time = getTickCount();
	
	SeedFillNew(binImage, labelImg);
	time = 1000 * ((double)getTickCount() - time) / getTickFrequency();
	cout << std::fixed << time << "ms" << endl;
	//彩色显示
	/*
	cv::Mat colorLabelImg;
	LabelColor(labelImg, colorLabelImg);
	cv::imshow("colorImg", colorLabelImg);
	*/
	//灰度显示
	cv::Mat grayImg;
	labelImg *= 10;
	labelImg.convertTo(grayImg, CV_8UC1);
	cv::imshow("labelImg", grayImg);
	double minval, maxval;
	minMaxLoc(labelImg, &minval, &maxval);
	cout << "minval" << minval << endl;
	cout << "maxval" << maxval << endl;
	cv::waitKey(0);
	return 0;
}

终于知道是啥原因了,我在进行Prewitt算子对边缘进行粗定位检测过后,没有进行去噪处理,一定要把图像转换为二值图像,就方便多了。并且还要记住,霍夫检测的直线像素是255白线才可以,经过长时间的试错终于解决了。输入原图像如下所示,我这里使用的去噪对比了四种,但是下面这种是最好的。Opencv 非局部降噪_51CTO博客_opencv降噪Opencv 非局部降噪,opencv自带的非局部降噪算法:CV_EXPORTS_WvoidfastNlMeansDenoising(InputArraysrc,OutputArraydst,floath=3,inttemplateWindowSize=7,intsearchWindowSize=21);h是过滤强度,templateWindowSize是分块大小,searchWindowSize是搜索区域大小。应用实例intmain(){MatI..https://blog.51cto.com/u_15458280/4843576

#include<opencv2/opencv.hpp>
#include<iostream>
using namespace std;
using namespace cv;

//数组从大到小排序
void reserve(int x[], int n) {
	int i, j, temp;
	for (i = 0; i < n - 1; i++) {     //一共n个元素,则需要比较n-1次
		for (j = 0; j < n - 1 - i; j++) {     //每一个元素需要比较的次数
			if (x[i] < x[i + j + 1]) {
				temp = x[i];
				x[i] = x[i + j + 1];
				x[i + j + 1] = temp;
			}
		}
	}
}

void add_salt_pepper_noise(Mat &image) {
	RNG rng(12345);
	int h = image.rows;
	int w = image.cols;
	int nums = 10000;
	for (int i = 0; i < nums; i++) {
		int x = rng.uniform(0, w);
		int y = rng.uniform(0, h);
		if (i % 2 == 1) {
			image.at<Vec3b>(y, x) = Vec3b(255, 255, 255);
		}
		else {
			image.at<Vec3b>(y, x) = Vec3b(0, 0, 0);
		}
	}
	imshow("salt pepper", image);
}

void gaussian_noise(Mat &image) {
	Mat noise = Mat::zeros(image.size(), image.type());
	randn(noise, (15, 15, 15), (30, 30, 30));
	Mat dst;
	add(image, noise, dst);
	imshow("gaussian noise", dst);
	dst.copyTo(image);
}

Mat convertTo3Channels(const Mat& binImg)
{
	Mat three_channel = Mat::zeros(binImg.rows, binImg.cols, CV_8UC3);
	vector<Mat> channels;
	for (int i = 0; i < 3; i++)
	{
		channels.push_back(binImg);
	}
	merge(channels, three_channel);
	return three_channel;
}

int main(int argc, char*argv[])
{
	//加载图像
	Mat img, gray_image, dst;
	img = imread("垂直边缘.jpg");
	 Mat img1 = imread("垂直边缘.jpg");
	//判断图像是否导入成功
	if (img.empty())
	{
		cout << "加载失败" << endl;
		return -1;
	}
	//显示图像
	namedWindow("original image", WINDOW_AUTOSIZE);
	imshow("original image", img);
	//转换灰度图像
	cvtColor(img, gray_image, COLOR_BGR2GRAY);
	//获取灰度图像宽度和高度
	int width = gray_image.cols;
	int height = gray_image.rows;
	//遍历像素值(单通道)
	for (int row = 0; row < height; row++)
	{
		for (int col = 0; col < width; col++)
		{
			int gray = gray_image.at<uchar>(row, col);
			gray_image.at<uchar>(row, col) = 255 - gray;	//图像取反
		};
	};
	namedWindow("inv_gray_image", WINDOW_AUTOSIZE);
	imshow("inv_gray_image", gray_image);


	Mat sh;
	
	fastNlMeansDenoising(gray_image, sh, 21, 7, 21);
	namedWindow("inv_gray_image1", WINDOW_AUTOSIZE);
	imshow("inv_gray_image1", sh);
	
	
	waitKey(50);

	
	//Mat s;
	//获取灰度图像宽度和高度
	 width = sh.cols;
	 height = sh.rows;
	//遍历像素值(单通道)
	for (int row = 0; row < height; row++)
	{
		for (int col = 0; col < width; col++)
		{
			int gray = sh.at<uchar>(row, col);
			sh.at<uchar>(row, col) = 255 - gray;	//图像取反
		};
	};
	namedWindow("inv_gray_image2", WINDOW_AUTOSIZE);
	imshow("inv_gray_image2", sh);

	
	cv::threshold(sh, sh, 50, 255, cv::THRESH_BINARY);
	cv::namedWindow("二值化", CV_WINDOW_NORMAL);
	imshow("二值化", sh);

	vector<Vec4i> lines;
	HoughLinesP(sh, lines, 1, CV_PI / 180, 50,100, 5);
	int Length[100] = { 0 };//存放直线长度
	for (size_t i = 0; i < lines.size(); i++)
	{
		Vec4i I = lines[i];
		double x1 = I[0];
		double y1 = I[1];
		double x2 = I[2];
		double y2 = I[3];

		//筛选满足条件的点
		if (abs(x1 - x2) + abs(y1 - y2) > 50)
		{
			Length[i] = sqrt((x1 - x2)*(x1 - x2) + (y1 - y2) * (y1 - y2));
			//将满足条件的点画出
			line(img, Point2d(x1, y1), Point2d(x2, y2), Scalar(0, 255, 255), 2);

			cout << " " << "(" << x1 << "," << y1 << ")" << " " << "(" << x2 << "," << y2 << ")" << endl;
			//line(canny, Point2d(x1, y1), Point2d(x2, y2), Scalar(0, 255, 255), 2);
		}
	}

	Mat imgShow;
	imgShow = img;
	resize(imgShow, imgShow, Size(imgShow.cols / 4, imgShow.rows / 4));
	imshow("imgShow", imgShow);
	imwrite("shuchu.png", imgShow);


	reserve(Length, 100);
	for (int i = 0; i < 100; i++) {
		cout << "长度" << Length[i] << endl;   //输出排序后的数组元素
	}

	for (size_t i = 0; i < lines.size(); i++)
	{
		Vec4i I = lines[i];
		double x1 = I[0];
		double y1 = I[1];
		double x2 = I[2];
		double y2 = I[3];
		cout << "sdjk" << endl;
		cout << int(sqrt((x1 - x2)*(x1 - x2) + (y1 - y2) * (y1 - y2))) << endl;
		//筛选满足条件的点
		if ((int(sqrt((x1 - x2)*(x1 - x2) + (y1 - y2) * (y1 - y2))) == Length[0]) || (int(sqrt((x1 - x2)*(x1 - x2) + (y1 - y2) * (y1 - y2))) == Length[1]))
		{

			//将满足条件的点画出
			line(img1, Point2d(x1, y1), Point2d(x2, y2), Scalar(0, 255, 255), 2);

			cout << "djfkljsa " << "(" << x1 << "," << y1 << ")" << " " << "(" << x2 << "," << y2 << ")" << endl;
			//line(canny, Point2d(x1, y1), Point2d(x2, y2), Scalar(0, 255, 255), 2);
		}
	}

	imgShow = img1;
	resize(imgShow, imgShow, Size(imgShow.cols / 4, imgShow.rows / 4));
	imshow("imgShow2", imgShow);
	imwrite("shuchu2.png", imgShow);
	waitKey(0);
	return 0;
};


结果图如下所示:

 

 终于弄出来了,去干饭。

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值