OpenCV 霍夫线
官方有两个算子,HoughLines和HoughLinesP
HoughLines得到两个变量:θ、ρ
在旋转矫正时,使用的是angle。具体见下图。
θ 弧度制,[0,π)
angle 角度制,[-90°,90°),正值则逆时针旋转
下图中的angle是负值(需顺时针选择),angle=θ*180/π - 90
#include <iostream> #include<opencv.hpp> using namespace cv; #include <numeric> //for accumulate int main() { cv::Mat src = cv::imread("C:/line.bmp"); //灰度化 cv::Mat gray; cv::cvtColor(src, gray, cv::COLOR_BGR2GRAY); //二值化,得到1和255的图 Mat binary; threshold(gray, binary, 0, 255, cv::THRESH_BINARY_INV | cv::THRESH_OTSU); imshow("binary", binary); //霍夫线 std::vector<cv::Vec2f> lines; cv::HoughLines(binary, lines, 1.0, CV_PI / 180, 100, 0, 0); int numLines = lines.size(); std::vector<float> thetas; for (int l = 0; l < numLines; l++) { float rho = lines[l][0]; //ρ float theta = lines[l][1]; //θ cv::Point pt1, pt2; double a = cos(theta), b = sin(theta); double x0 = a * rho, y0 = b * rho; pt1.x = cvRound(x0 + 1000 * (-b)); pt1.y = cvRound(y0 + 1000 * (a)); pt2.x = cvRound(x0 - 1000 * (-b)); pt2.y = cvRound(y0 - 1000 * (a)); line(binary, pt1, pt2, cv::Scalar(255), 1); //在二值图上画线 thetas.push_back(theta); } cv::imshow("Hough直线", binary); //角度均值 float sum = std::accumulate(std::begin(thetas), std::end(thetas), 0.0); float meanTheta = sum / thetas.size(); float meanAngle = (meanTheta / CV_PI) * 180 - 90; //矫正 Point2f center; //旋转中心为图像中心 center.x = float(src.cols / 2.0); center.y = float(src.rows / 2.0); Mat M = getRotationMatrix2D(center, meanAngle, 1); //计算二维旋转的仿射变换矩阵 Mat img_rotate; int length = 0; length = sqrt(src.cols * src.cols + src.rows * src.rows); M.at<double>(0,2) += (length - src.size().width) / 2.0; //平移,仿射变换矩阵2*3,第3列代表平移 M.at<double>(1,2) += (length - src.size().height) / 2.0; warpAffine(src, img_rotate, M, Size(length, length), 1, 0, Scalar(255, 255, 255));//仿射变换,背景色填充为白色 //展示 imshow("img_rotate", img_rotate); waitKey(); }
【探讨点】
θ∈[0,π),在π时会突变为0,存在临界情况。这就导致在求多条线角度平均值的时候会产生错误情况。
如下图三条线,θ均值应该为0,但是计算为π/3
目前还没有找到合理的方法求角度均值,暂时解决如下:
适用情况:线条角度偏差不大时,那么在临界左右,极差会很大,靠近π的要减去π后再求均值
//直接计算均值,会在180°附近出现问题。以下计算方式有个假设:直线方向差异不大 //极差超过一定值的,靠近180°的要-180° float max = *std::max_element(thetas.begin(), thetas.end()); float min = *std::min_element(thetas.begin(), thetas.end()); if ((max - min) > CV_PI / 2) { for (auto& i : thetas) //引用,效率更高。i被改变则对应的元素被改变 { if (CV_PI - i < 0.02) //接近180°时 { i -= CV_PI; } } }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
2020-02-21 并行计算 cv::parallel_for_() 函数