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),一條直線能夠通過在平面 -
尋找交於一點的曲線數量來檢測
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
等