*提取直线、轮廓和区域
之前的二值边缘分布图有两个缺点。首先,检测到的边缘过厚,这导致更加难以识别物体的边界;第二,通常不能找到这样的阈值:低到足以检测到图像中的所有重要的边缘同时又避免产生太多无关紧要的边缘。Canny算法试图解决这样的问题。
使用cv::Canny()函数需要给出低阈值和高阈值两个阈值。canny算子通常是基于sobel算子,低阈值是宽松阈值,很多不需要的也被检测出来了;高阈值则界定重要轮廓边缘,canny算法结合这两种边缘分布图生成最优的轮廓分布图。
基于现在无人驾驶的火爆,我下载了一张道路图:
使用canny算子检测到的轮廓:
代码:
int main()
{
cv::Mat image = cv::imread("road.jpg");
cv::imshow("original image", image);
cv::Mat contours;
cv::Canny(image, contours, 350, 400);
cv::imshow("Canny Image", contours);
cvWaitKey();
}
在霍夫变换中,用这个方程式表示直线:
基础霍夫变化:CV::HoughLines,它输入一个二值分布图,通常是一个已经生成的边缘分布图,例如用Canny算子生成的分布图,输出的是一个cv::Vec2f的向量,每个元素是一对浮点数,表示检测到直线的( rho, theta )
这个算法检测的是图像中的直线而不是线段,不会给出直线的端点。
检测结果:
代码:
int main()
{
cv::Mat image = cv::imread("road2.jpg");
cv::imshow("original image", image);
cv::Mat contours;
cv::Canny(image, contours, 350, 400);
//用霍夫变换检测直线
std::vector<cv::Vec2f> lines;
cv::HoughLines(contours, lines,
1, PI / 180,//步长
60);//最小投票数
std::vector<cv::Vec2f>::const_iterator it = lines.begin();
while (it!=lines.end())
{
float rho = (*it)[0];
float theta = (*it)[1];
if (theta<PI / 4.0 || theta>3.0*PI / 4.0)
{
//直线与第一行的交叉点
cv::Point pt1(static_cast<int>(rho / cos(theta)), 0.0);
//直线与最后一行的交叉点
cv::Point pt2(rho / cos(theta) - image.rows*sin(theta) / cos(theta), image.rows);
cv::line(image, pt1, pt2, cv::Scalar(255,255,255), 1);
}
else
{
cv::Point pt1(0, rho / sin(theta));
cv::Point pt2(image.cols, rho / sin(theta) - image.cols*cos(theta) / sin(theta));
cv::line(image, pt1, pt2, cv::Scalar(255,255,255), 1);
}
it++;
}
cv::imshow