HoughLinesP参数分析

void HoughLinesP(InputArray image,OutputArray lines, double rho, double theta, int threshold, double minLineLength=0,double maxLineGap=0 )

image为输入图像,要求是单通道,8位图像

lines为输出参数,4个元素表示,即直线的起始和终止端点的4个坐标(x1,y1),(x2,y2)

rho为距离分辨率,一般为1

heta为角度的分辨率,一般CV_PI/180

threshold为阈值,hough变换图像空间值最大点,大于则执行

minLineLength为最小直线长度(像素),即如果小于该值,则不被认为是一条直线

maxLineGap为直线间隙最大值,如果两条直线间隙大于该值,则被认为是两条线段,否则是一条。


Houghlines此函数可以找出采用标准霍夫变换的二值图像线条。在OpenCV中,我们可以用其来调用标准霍夫变换SHT和多尺度霍夫变换MSHT的OpenCV内建算法。

[cpp]  view plain  copy

1. C++: void HoughLines(InputArray image, OutputArray lines, double rho, double theta, int threshold, double srn=0, double stn=0 )



  • 第一个参数,InputArray类型的image,输入图像,即源图像,需为8位的单通道二进制图像,可以将任意的源图载入进来后由函数修改成此格式后,再填在这里。
  • 第二个参数,InputArray类型的lines,经过调用HoughLines函数后储存了霍夫线变换检测到线条的输出矢量。每一条线由具有两个元素的矢量表示,其中,是离坐标原点((0,0)(也就是图像的左上角)的距离。 是弧度线条旋转角度(0~垂直线,π/2~水平线)。
  • 第三个参数,double类型的rho,以像素为单位的距离精度。另一种形容方式是直线搜索时的进步尺寸的单位半径。PS:Latex中/rho就表示 。
  • 第四个参数,double类型的theta,以弧度为单位的角度精度。另一种形容方式是直线搜索时的进步尺寸的单位角度。
  • 第五个参数,int类型的threshold,累加平面的阈值参数,即识别某部分为图中的一条直线时它在累加平面中必须达到的值。大于阈值threshold的线段才可以被检测通过并返回到结果中。
  • 第六个参数,double类型的srn,有默认值0。对于多尺度的霍夫变换,这是第三个参数进步尺寸rho的除数距离。粗略的累加器进步尺寸直接是第三个参数rho,而精确的累加器进步尺寸为rho/srn。
  • 第七个参数,double类型的stn,有默认值0,对于多尺度霍夫变换,srn表示第四个参数进步尺寸的单位角度theta的除数距离。且如果srn和stn同时为0,就表示使用经典的霍夫变换。否则,这两个参数应该都为正数。

仿射变换与透视变换的区别:

(1)、仿射变换,它可以将矩形转换成平行四边形,也可以将矩形的边压扁但必须保持边是平行的,也可以将矩形旋转或者按比例缩放。透视变换除了能够处理仿射变换的操作外,还可以将矩形转换成梯形。即仿射变换后还是平行四边形,透视变换后是四边形。因此可以说仿射变换是透视变换的一个子集。

         (2)、在OpenCV中,基于2*3矩阵进行的变换,是图像的仿射变换;基于3*3矩阵进行的变换,是图像的透视变换或者单应性映射。图像透视变换多用于图像校正




opencv 检测斜线_霍夫变换





getPerspectiveTransform函数

函数作用:

根据输入和输出点获得图像透视变换的矩阵

函数的调用形式:

C++: Mat getPerspectiveTransform(InputArray src, InputArray dst)

InputArray src, InputArray dst:分别为变换的输入和输出点

分别四个点

warpPerspective函数

主要作用:对图像进行透视变换,就是变形

warpPerspective函数

主要作用:对图像进行透视变换,就是变形






函数的调用形式:

C++: void warpPerspective(InputArray src, OutputArray dst, InputArray M, Size dsize, int flags=INTER_LINEAR, int borderMode=BORDER_CONSTANT, const Scalar& borderValue=Scalar())

参数详解:

InputArray src:输入的图像

OutputArray dst:输出的图像

InputArray M:透视变换的矩阵

Size dsize:输出图像的大小

int flags=INTER_LINEAR:输出图像的插值方法,

combination of interpolation methods (INTER_LINEAR or INTER_NEAREST) and the optional flagWARP_INVERSE_MAP, that sets M as the inverse transformation (  )

int borderMode=BORDER_CONSTANT:图像边界的处理方式

const Scalar& borderValue=Scalar():边界的颜色设置,一般默认是0


void morphologyEx(InputArray src, OutputArray dst, int op, InputArray kernel, Point anchor=Point(-1,-1), intiterations=1, int borderType=BORDER_CONSTANT, const Scalar& borderValue=morphologyDefaultBorderValue() )

函数形式:


函数参数:

  • 第一个参数,InputArray类型的src,输入图像,即源图像,填Mat类的对象即可。图像位深应该为以下五种之一:CV_8U, CV_16U,CV_16S, CV_32F 或CV_64F。
  • 第二个参数,OutputArray类型的dst,即目标图像,函数的输出参数,需要和源图片有一样的尺寸和类型。
  • 第三个参数,int类型的op,表示形态学运算的类型,可以是如下之一的标识符:
  • MORPH_OPEN – 开运算(Opening operation)
  • MORPH_CLOSE – 闭运算(Closing operation)
  • MORPH_GRADIENT -形态学梯度(Morphological gradient)
  • MORPH_TOPHAT - “顶帽”(“Top hat”)
  • MORPH_BLACKHAT - “黑帽”(“Black hat“)


另有CV版本的标识符也可选择,如CV_MOP_CLOSE,CV_MOP_GRADIENT,CV_MOP_TOPHAT,CV_MOP_BLACKHAT,这应该是OpenCV1.0系列版本遗留下来的标识符,和上面的“MORPH_OPEN”一样的效果。


 


  • 第四个参数,InputArray类型的kernel,形态学运算的内核。若为NULL时,表示的是使用参考点位于中心3x3的核。我们一般使用函数 getStructuringElement配合这个参数的使用。getStructuringElement函数会返回指定形状和尺寸的结构元素(内核矩阵)。关于getStructuringElement我们上篇文章中讲过了,这里为了大家参阅方便,再写一遍:



其中,getStructuringElement函数的第一个参数表示内核的形状,我们可以选择如下三种形状之一:



  • 矩形: MORPH_RECT
  • 交叉形: MORPH_CROSS
  • 椭圆形: MORPH_ELLIPSE



而getStructuringElement函数的第二和第三个参数分别是内核的尺寸以及锚点的位置。



我们一般在调用erode以及dilate函数之前,先定义一个Mat类型的变量来获得getStructuringElement函数的返回值。对于锚点的位置,有默认值Point(-1,-1),表示锚点位于中心。且需要注意,十字形的element形状唯一依赖于锚点的位置。而在其他情况下,锚点只是影响了形态学运算结果的偏移。


  • 第五个参数,Point类型的anchor,锚的位置,其有默认值(-1,-1),表示锚位于中心。
  • 第六个参数,int类型的iterations,迭代使用函数的次数,默认值为1。
  • 第七个参数,int类型的borderType,用于推断图像外部像素的某种边界模式。注意它有默认值BORDER_ CONSTANT。
  • 第八个参数,const Scalar&类型的borderValue,当边界为常数时的边界值,有默认值morphologyDefaultBorderValue(),一般我们不用去管他。需要用到它时,可以看官方文档中的createMorphologyFilter()函数得到更详细的解释。





#include <opencv2/opencv.hpp>
#include <iostream>
#include <math.h>

using namespace cv;
using namespace std;

int main(int argc, char** argv) {
	Mat src = imread("D:/gloomyfish/case5.png");
	if (src.empty()) {
		printf("could not load image...\n");
		return 0;
	}
	namedWindow("input image", CV_WINDOW_AUTOSIZE);
	imshow("input image", src);

	// 二值处理
	Mat gray_src, binary, dst;
	cvtColor(src, gray_src, COLOR_BGR2GRAY);
	threshold(gray_src, binary, 0, 255, THRESH_BINARY_INV | THRESH_OTSU);
	imshow("binary image", binary);

	// 形态学操作
	Mat kernel = getStructuringElement(MORPH_RECT, Size(5, 5), Point(-1, -1));
	morphologyEx(binary, dst, MORPH_CLOSE, kernel, Point(-1, -1), 3);
	imshow("morphology", dst);

	// 轮廓发现
	bitwise_not(dst, dst, Mat());
	vector<vector<Point>> contours;
	vector<Vec4i> hireachy;
	findContours(dst, contours, hireachy, CV_RETR_TREE, CHAIN_APPROX_SIMPLE, Point());

	// 轮廓绘t制
	int width = src.cols;
	int height = src.rows;
	Mat drawImage = Mat::zeros(src.size(), CV_8UC3);
	for (size_t t = 0; t < contours.size(); t++) {
		Rect rect = boundingRect(contours[t]);
		if (rect.width > width / 2 && rect.width < width-5) {
			drawContours(drawImage, contours, static_cast<int>(t), Scalar(0, 0, 255), 2, 8, hireachy, 0, Point());
		}
	}
	imshow("contours", drawImage);

	vector<Vec4i> lines;
	Mat contoursImg;
	int accu = min(width*0.5, height*0.5);
	cvtColor(drawImage, contoursImg, COLOR_BGR2GRAY);
	HoughLinesP(contoursImg, lines, 1, CV_PI / 180.0, accu, accu, 0);
	Mat linesImage = Mat::zeros(src.size(), CV_8UC3);
	for (size_t t = 0; t < lines.size(); t++) {
		Vec4i ln = lines[t];
		line(linesImage, Point(ln[0], ln[1]), Point(ln[2], ln[3]), Scalar(0, 0, 255), 2, 8, 0);
	}
	printf("number of lines : %d\n", lines.size());
	imshow("lines image", linesImage);

	// 寻找与定位上下左右四条直线
	int deltah = 0;
	Vec4i topLine, bottomLine;
	Vec4i leftLine, rightLine;
	for (int i = 0; i < lines.size(); i++) {
		Vec4i ln = lines[i];
		deltah = abs(ln[3] - ln[1]);
		if (ln[3] < height / 2.0 && ln[1] < height / 2.0 && deltah < accu - 1) {
			if (topLine[3] > ln[3] && topLine[3]>0) {
				topLine = lines[i];
			} else {
				topLine = lines[i];
			}
		}
		if (ln[3] > height / 2.0 && ln[1] > height / 2.0 && deltah < accu - 1) {
			bottomLine = lines[i];
		}
		if (ln[0] < width / 2.0 && ln[2] < width/2.0) {
			leftLine = lines[i];
		}
		if (ln[0] > width / 2.0 && ln[2] > width / 2.0) {
			rightLine = lines[i];
		}
	}
	cout << "top line : p1(x, y) = " << topLine[0] << "," << topLine[1] << " p2(x, y) = " << topLine[2] << "," << topLine[3] << endl;
	cout << "bottom line : p1(x, y) = " << bottomLine[0] << "," << bottomLine[1] << " p2(x, y) = " << bottomLine[2] << "," << bottomLine[3] << endl;
	cout << "left line : p1(x, y) = " << leftLine[0] << "," << leftLine[1] << " p2(x, y) = " << leftLine[2] << "," << leftLine[3] << endl;
	cout << "right line : p1(x, y) = " << rightLine[0] << "," << rightLine[1] << " p2(x, y) = " << rightLine[2] << "," << rightLine[3] << endl;

	// 拟合四条直线方程
	float k1, c1;
	k1 = float(topLine[3] - topLine[1]) / float(topLine[2] - topLine[0]);
	c1 = topLine[1] - k1*topLine[0];
	float k2, c2;
	k2 = float(bottomLine[3] - bottomLine[1]) / float(bottomLine[2] - bottomLine[0]);
	c2 = bottomLine[1] - k2*bottomLine[0];
	float k3, c3;
	k3 = float(leftLine[3] - leftLine[1]) / float(leftLine[2] - leftLine[0]);
	c3 = leftLine[1] - k3*leftLine[0];
	float k4, c4;
	k4 = float(rightLine[3] - rightLine[1]) / float(rightLine[2] - rightLine[0]);
	c4 = rightLine[1] - k4*rightLine[0];

	// 四条直线交点
	Point p1; // 左上角
	p1.x = static_cast<int>((c1 - c3) / (k3 - k1));
	p1.y = static_cast<int>(k1*p1.x + c1);
	Point p2; // 右上角
	p2.x = static_cast<int>((c1 - c4) / (k4 - k1));
	p2.y = static_cast<int>(k1*p2.x + c1);
	Point p3; // 左下角
	p3.x = static_cast<int>((c2 - c3) / (k3 - k2));
	p3.y = static_cast<int>(k2*p3.x + c2);
	Point p4; // 右下角
	p4.x = static_cast<int>((c2 - c4) / (k4 - k2));
	p4.y = static_cast<int>(k2*p4.x + c2);
	cout << "p1(x, y)=" << p1.x << "," << p1.y << endl;
	cout << "p2(x, y)=" << p2.x << "," << p2.y << endl;
	cout << "p3(x, y)=" << p3.x << "," << p3.y << endl;
	cout << "p4(x, y)=" << p4.x << "," << p4.y << endl;

	// 显示四个点坐标
	circle(linesImage, p1, 2, Scalar(255, 0, 0), 2, 8, 0);
	circle(linesImage, p2, 2, Scalar(255, 0, 0), 2, 8, 0);
	circle(linesImage, p3, 2, Scalar(255, 0, 0), 2, 8, 0);
	circle(linesImage, p4, 2, Scalar(255, 0, 0), 2, 8, 0);
	line(linesImage, Point(topLine[0], topLine[1]), Point(topLine[2], topLine[3]), Scalar(0, 255, 0), 2, 8, 0);
	imshow("four corners", linesImage);

	// 透视变换


vector<Point2f> src_corners(4);
	src_corners[0] = p1;
	src_corners[1] = p2;
	src_corners[2] = p3;
	src_corners[3] = p4;


	cout << src_corners[3] << endl;
	cout << topline << endl;
	
	vector<Point2f> dst_corners(4);
	dst_corners[0] = Point(0, 0);
	dst_corners[1] = Point(src.cols, 0);
	dst_corners[2] = Point(src.cols, src.rows*0.8);//控制显示内容的大小
	dst_corners[3] = Point(0, src.rows*0.8);
	/*Mat resultimage;
	Mat warpmatrix = getPerspectiveTransform(src_corners, dst_corners);
	warpPerspective(src, resultimage, warpmatrix, resultimage.size(), INTER_LINEAR);
	imshow("Result", resultimage);*/
	Mat resultImage;
	Mat warpmatrix = getPerspectiveTransform(src_corners, dst_corners);
	cvtColor(src, src, COLOR_GRAY2BGR);
	warpPerspective(src, resultImage, warpmatrix, Size(src.cols,src.rows*0.8), INTER_LINEAR);//这个size控制显示图片大小
	namedWindow("Final Result", CV_WINDOW_AUTOSIZE);
	imshow("Final Result", resultImage);
	waitKey(0);




waitKey(0);return 0;}