case5 opencv中透视变换结合直线检测对图像进行校正
2018-01-15 09:31
573 查看
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为距离分辨率,一般为1heta为角度的分辨率,一般CV_PI/180threshold为阈值,hough变换图像空间值最大点,大于则执行minLineLength为最小直线长度(像素),即如果小于该值,则不被认为是一条直线maxLineGap为直线间隙最大值,如果两条直线间隙大于该值,则被认为是两条线段,否则是一条。
Houghlines此函数可以找出采用标准霍夫变换的二值图像线条。在OpenCV中,我们可以用其来调用标准霍夫变换SHT和多尺度霍夫变换MSHT的OpenCV内建算法。[cpp] view plain copyC++: 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矩阵进行的变换,是图像的透视变换或者单应性映射。图像透视变换多用于图像校正
getPerspectiveTransform函数函数作用:根据输入和输出点获得图像透视变换的矩阵函数的调用形式:C++: Mat getPerspectiveTransform(InputArray src, InputArray dst)
InputArray src, InputArray dst:分别为变换的输入和输出点
分别四个点warpPerspective函数主要作用:对图像进行透视变换,就是变形warpPerspective函数主要作用:对图像进行透视变换,就是变形 http://blog.csdn.net/qq_18343569/article/details/47953843
函数的调用形式: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()函数得到更详细的解释。
waitKey(0);return 0;}
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为距离分辨率,一般为1heta为角度的分辨率,一般CV_PI/180threshold为阈值,hough变换图像空间值最大点,大于则执行minLineLength为最小直线长度(像素),即如果小于该值,则不被认为是一条直线maxLineGap为直线间隙最大值,如果两条直线间隙大于该值,则被认为是两条线段,否则是一条。
Houghlines此函数可以找出采用标准霍夫变换的二值图像线条。在OpenCV中,我们可以用其来调用标准霍夫变换SHT和多尺度霍夫变换MSHT的OpenCV内建算法。[cpp] view plain copyC++: 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矩阵进行的变换,是图像的透视变换或者单应性映射。图像透视变换多用于图像校正
getPerspectiveTransform函数函数作用:根据输入和输出点获得图像透视变换的矩阵函数的调用形式:C++: Mat getPerspectiveTransform(InputArray src, InputArray dst)
InputArray src, InputArray dst:分别为变换的输入和输出点
分别四个点warpPerspective函数主要作用:对图像进行透视变换,就是变形warpPerspective函数主要作用:对图像进行透视变换,就是变形 http://blog.csdn.net/qq_18343569/article/details/47953843
函数的调用形式: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;}
相关文章推荐
- 用opencv计算棋盘格内角点坐标(通过多个内角点获得转换矩阵),并同时用halcon和opnecv对图像进行透视变换
- 在Android平台上利用opencv进行图像处理之边缘检测、灰度变换、缩小
- c++结合opencv进行人脸检测
- android openCV检测图像的基本特征,包括Canny边缘检测、Harris角点检测、霍夫直线检测-基于Android studio
- 结合OpenCV摄像头使用Dlib库进行人脸检测及标注特征点和提取人脸特征-Python
- 在opencv3中利用SVM进行图像目标检测和分类
- 对倾斜的图像进行修正——基于opencv 透视变换
- 在OpenCV下利用霍夫变换进行直线检测和圆检测
- 用opencv计算棋盘格内角点坐标(通过4个内角点获得转换矩阵),并同时用halcon和opnecv对图像进行透视变换
- Java调用OpenCV进行Hough变换直线检测
- opencv对图像进行边缘及角点检测
- c++结合opencv进行人脸检测
- Android平台上利用opencv进行图像的边沿检测
- 利用Opencv中的Houghline方法进行直线检测---python语言
- 在opencv3中利用SVM进行图像目标检测和分类
- 题目:opencv下对图像进行图像模糊(均值滤波)处理和边缘检测
- openCV实现图像的直线检测
- opencv采集图像并进行轮廓检测
- 运用ROS和OpenCV进行简单的图像处理之检测
- Python3与OpenCV3.3 图像处理(十九)--直线检测