图片的倾斜检测

1、原理:

计算图片里主要少数线条的平均角度,大于某个值则认为倾斜

2、效果图:

倾斜的(设角度大于12度为倾斜)

 正常的

 

 3、流程

a、先对图片进行大小缩放,加快处理速度

Mat srcPic = imread(pInFileName);
	int cols=configText.resizeCols;
	int rows=srcPic.rows/(srcPic.cols*1.00/cols);//按宽高比例缩放
	Mat src =Mat::zeros(rows,cols , CV_8UC3);
	resize(srcPic,src,src.size());

b、如果要检索的图片具有共同的特征(都是货架且货架颜色相同)可采用阈值化二分,让边缘检测更有方向性(慎用!

threshold(srcImage, srcThrImage, 40, 255, THRESH_BINARY);
	imshow("阈值化后的效果图", srcThrImage);

c、通过Canny算子进行边缘检测

高阈值求边缘低阈值平滑边缘,可以将高低阈值的比率设置为足够大如factor=5使得边缘更加的平滑

Canny(srcThrImage, midImage, cannyThreshold,cannyThreshold*factor);//Canny边缘检测算法,指定高低阈值(梯度幅值)

d、根据边缘用HoughLinesP霍夫线变换检测直线,最终需要控制检测到的直线条数

方法说明:

霍夫变换使用极坐标来表示直线(极径rho和极角theta),一条直线能够通过在平面 \theta - r 寻找交于一点的曲线数量来检测

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

rho:就是一个半径的分辨率
theta:角度分辨率
threshold:判断直线点数的阈值
minLineLength:线段长度阈值
minLineGap:线段上最近两点之间的阈值(不大于此阈值则会认为是同一条直线上的点)

注意:

1、参数 threshold、minLineLength的设置很重要,关系到是否能过检测到线段及数量,需要根据目标图片的大小特征实验得到

2、minLineGap建议设置为2会使实际的轮廓更多的识别为线段,太大也会导致识别太多的错误线段。

HoughLinesP(midImage, lines, 1, CV_PI / 180, configText.thresholdVal, configText.minLineLength, configText.maxLineGap);//HoughLinesP霍夫线变换
	while (lines.size()>10)//为了保持检查到的直线条数需要对threshold进行递减或递增
	{
		cannyThreshold+=10;
		Canny(srcThrImage, midImage, cannyThreshold,cannyThreshold*factor);
		cvtColor(midImage, dstImage, CV_GRAY2BGR);
		HoughLinesP(midImage, lines, 1, CV_PI / 180, configText.thresholdVal, configText.minLineLength, configText.maxLineGap);
	}
	if (!lines.size())
	{
		cannyThreshold-=10;
		Canny(srcThrImage, midImage, cannyThreshold,cannyThreshold*factor);
		cvtColor(midImage, dstImage, CV_GRAY2BGR);
		HoughLinesP(midImage, lines, 1, CV_PI / 180, configText.thresholdVal, configText.minLineLength, configText.maxLineGap);
	}

e、对检测到的线条计算平均角度用来判断是否倾斜

注意:

1、检测到最大角度不会超过45度

2、保证检测到的线条数足够多(如50,太多会有很多无效的线段导致最终结果无意义),让计算的平均角度与实际更加的符合

double DetectionSlope::GetAvgAngle(const vector<Vec4i>& lines){//通过直线求平均角度
	double totalAngle=0;
	for (auto & line : lines)
	{
		//360度=2π弧度
		auto radian=atan(abs((line[3]-line[1])*1.0/(line[2]-line[0])));
		auto angle=abs(360*radian/(2*CV_PI));
		if(angle>45)
			angle=abs(90-angle);
		totalAngle+=angle;
	}
	return totalAngle/lines.size();
}

4、补充

a、对于没有扭曲只是角度倾斜的图片可通过旋转来修正

//逆时针旋转图像degree角度(原尺寸)    
void rotateImage(Mat src, Mat& img_rotate, double degree)
{
	//旋转中心为图像中心    
	Point2f center;
	center.x = float(src.cols / 2.0);
	center.y = float(src.rows / 2.0);
	int length = 0;
	length = sqrt(src.cols*src.cols + src.rows*src.rows);
	//计算二维旋转的仿射变换矩阵  
	Mat M = getRotationMatrix2D(center, degree, 1);//获取旋转矩阵
	warpAffine(src, img_rotate, M, Size(length, length), 1, 0, Scalar(255,255,255));//仿射变换,背景色填充为白色  
}

b、对于存在扭曲的图片进行修正需要找到 四个角的坐标点,一般认为是检测到的直线的四个角度(这个不太靠谱,找准实际的四个点比较难)

通过检测到的四个坐标点,形成源与目标的变换矩阵,再对图片进行透视变换

	Mat m1 = Mat(srcTriangle);
	Mat m2 = Mat(dstTriangle);
	Mat status;
	Mat h = findHomography(m1, m2, status, 0, 3);//获取单映射H(理解为两帐图片的点映射)
	warpPerspective(this->srcImage, this->dstImage, h, this->dstImage.size());//对图像进行透视变换,就是变形

 

 

 

 

参考:

Canny边缘检测

http://www.opencv.org.cn/opencvdoc/2.3.2/html/doc/tutorials/imgproc/imgtrans/canny_detector/canny_detector.html#canny-detector 

threshold阈值操作

http://www.opencv.org.cn/opencvdoc/2.3.2/html/doc/tutorials/imgproc/threshold/threshold.html#basic-threshold

霍夫线变换

http://www.opencv.org.cn/opencvdoc/2.3.2/html/doc/tutorials/imgproc/imgtrans/hough_lines/hough_lines.html#hough-lines

图像旋转

https://blog.csdn.net/guduruyu/article/details/70799804

Homography单映射

https://blog.csdn.net/liuphahaha/article/details/50719275

perspectiveTransform透视变换

https://blog.csdn.net/xiaowei_cqu/article/details/26478135

对倾斜的图像进行修正——基于opencv 透视变换 

https://blog.csdn.net/MrCharles/article/details/73739693

极坐标

https://zhuanlan.zhihu.com/p/26172668

 

posted on 2018-04-12 08:10  村_长  阅读(1977)  评论(0编辑  收藏  举报

导航