//img_bin:输入二值图,背景0,前景非0。
//line:直线方程标准式参数Ax+By+C=0,line[0]x+line[1]y+line[2]=0。注意:x,y指图像横纵坐标,分别对应width,height。
//成功返回真失败返回假
//抗干扰能力较好,低信噪比条件下也能有不错的效果。
bool RansacLine(cv::Mat& img_bin, float* line)
{
if (img_bin.empty() || img_bin.channels() != 1)
return false;
std::vector<cv::Point> points;
for (int i = 0;i < img_bin.rows;i++)
{
uchar* pb = img_bin.ptr<uchar>(i);
for (int j = 0;j < img_bin.cols;j++)
{
if (pb[j] > 0)//前景,根据情况修改条件,收集所有目标点
points.push_back(cv::Point(j, i));
}
}
if (points.size() == 0)
return false;
int distance_thres = 2;//满足集内条件,即以当前点到直线距离不大于distance_thres认定为该点在直线上。
int loop_count = 0;
int iner_count = 0;
int max_iner_count = 0;
float distance = 0;
float sqrt_ = 0;
int rand_k1 = 0, rand_k2 = 0;
cv::Point point1, point2;
float A = 0, B = 0, C = 0;
float max_dis = 0, min_dis = 100000, average_dis = 0;
while (loop_count++ < 10000)//最大循环次数,根据情况可设定为所有点数的10倍
{
srand((unsigned int)time(0)); //以当前系统时间来初始化srand种子
rand_k1 = rand() % points.size();
point1 = points[rand_k1];
rand_k2 = rand() % points.size();
while (rand_k1 == rand_k2)
rand_k2 = rand() % points.size();
point2 = points[rand_k2];
A = point2.y - point1.y;
B = point1.x - point2.x;
C = point1.x * (point1.y - point2.y) + point1.y * (point2.x - point1.x);
sqrt_ = sqrtf(A * A + B * B);
iner_count = 0;
float max_dis_ = 0, min_dis_ = 100000, sum_dis = 0;
for (int i = 0;i < points.size();i++)
{
distance = abs(points[i].x * A + points[i].y * B + C) * 1.0 / sqrt_;
if (distance < distance_thres)
iner_count++;
if (max_dis_ < distance)
max_dis_ = distance;
if (min_dis_ > distance)
min_dis_ = distance;
sum_dis += distance;
}
if (iner_count >= max_iner_count)
{
max_iner_count = iner_count;
line[0] = A;
line[1] = B;
line[2] = C;
max_dis = max_dis_;
min_dis = min_dis_;
average_dis = sum_dis / points.size();
}
}
std::cout << u8"Ransac line 最大误差:" << max_dis << u8" 最小误差:" << min_dis << u8" 平均误差:" << average_dis << std::endl;
return true;
}
//使用示例:
//float line[3];///直线方程:line[0]*x+line[1]*y+line[2]=0
if (RansacLine(img_bin, line))
{
cv::Point point1, point2;
if (line[0] == 0 && line[1] != 0)
{
point1 = cv::Point(0, -1 * line[2] / line[1]);
point2 = cv::Point(img_bin.cols - 1, -1 * line[2] / line[1]);
}
else if (line[0] != 0 && line[1] == 0)
{
point1 = cv::Point(-1 * line[2] / line[0], 0);
point2 = cv::Point(-1 * line[2] / line[0], img_bin.rows - 1);
}
else
{
point1 = cv::Point(0, -1 * line[2] / line[1]);
point2 = cv::Point(img_bin.cols, -1 * (img_bin.cols * line[0] + line[2]) / line[1]);
}
std::cout << "line: " << line << std::endl;
cv::line(img_bin, point1, point2, cv::Scalar(0, 255, 0));
cv::namedWindow("Ransac line", 0);
imshow("Ransac line", img_bin);
cv::waitKey(0);
}
else
{
std::cout << u8"寻找直线失败,请确认图像,并调整提取前景的阈值" << std::endl;
}
}
效果: