直线检测算法
直线检测算法中Hough算法是比较常用的一种, 在opencv中Hough变换有多个函数。
void HoughLines(InputArray image, OutputArray lines, double rho, double theta, int threshold, double srn=0, double stn=0 )
void HoughLinesP(InputArray image, OutputArray lines, double rho, double theta, int threshold, double minLineLength=0, double maxLineGap=0 )
HoughLines(),可以返回检测的直线坐标对,通过坐标对画出直线。
HoughLinesP(),在HoughLines的基础上加入了概率,并且可以通过设置判断直线间的距离将相近的多条直线合成一条直线。
虽然Hough检测不受断线的影响,但Hough变换检测的鲁棒性不强,如果图像中出现下列几种情况则检测效果会很差:
1.直线过粗(会出现检测出多条直线的情况,虽然HoughLineP可以一定程度解决这个问题,但是阈值的设置与输入的图片十分相关)
2.直线有角度变换(是指拍摄后无校正的图像,直线会出现扭曲的情况)
3.除了目标直线外,有干扰直线
单向准连通的表格检测直线算法
《单向准连通的表格检测直线算法》一文中,提出了一种检测算法。用这种算法检测直线可以很好的解决以下问题:
1.有断线的直线
2.有角度倾斜的直线
3.过粗的直线
并且通过阈值的设置,可以大体排除干扰直线。
基于Opencv的代码
以下图为例,输入二值化后的图像,需要检测出的目标区域为图中标注出的红色区域,检测的直线为区域中两条黑直线。最终实现目标为分割出两
直线间区域。
![]()
//遍历每一列的图像灰度值,查找每一行255的值
for (int col = 20; col < width - 20; ++col)
{
for (int row = 20; row < height - 20; ++row)
{
cnt_segment_line = 0;//记录纵向线段的个数
perPixelValue = binImg.at<uchar>(row, col); //at坐标是相反的
if (perPixelValue != 0){
int i = 1;
int j = 1;
}
perpendicular_line_len = 0;//每次垂直线段长度初始化赋值
detect_per_flag = 0;
tmp_beg_record_yiu = row; //设置初始y坐标
if (row == 2210&&col==49){
int x = 4;
}
if (perPixelValue == 255){
perpendicular_line_len = 1;
while (perPixelValue == 255){
if (row + 1 <= height - 20)
row = row + 1;//向下移动一格,判断接下来是不是黑色
else
break;
perPixelValue = binImg.at<uchar>(row, col); //at坐标是相反的
if (perPixelValue == 255){
perpendicular_line_len++;
tmp_beg_record_yid = row;
}
if (perpendicular_line_len <= 30){// 小于一个大阈值检测到可能是线段
detect_per_flag = 1;
}
else{ //过长则舍去
detect_per_flag = 0;
break;
}
}
}
if (detect_per_flag == 1){ //大于一个小阈值,可能是起始线段;且如果是直线上的点,则将值设置为0//开始线段的检测
if (perpendicular_line_len >= 0){
//record_detect_array[cnt_perpendicular_line][cnt_line_segment][0] = col;
//record_detect_array[cnt_perpendicular_line][cnt_line_segment][1] = tmp_beg_record_yiu;
//record_detect_array[cnt_perpendicular_line][cnt_line_segment][2] = tmp_beg_record_yid;
//cnt_line_segment++;
record_detect_array[cnt_perpendicular_line][cnt_segment_line][0] = col;
record_detect_array[cnt_perpendicular_line][cnt_segment_line][1] = tmp_beg_record_yiu;
record_detect_array[cnt_perpendicular_line][cnt_segment_line][2] = tmp_beg_record_yid;
cnt_segment_line++;
/*for (int tmp_hue_change = tmp_beg_record_yiu; tmp_hue_change <= tmp_beg_record_yid; tmp_hue_change++){
binImg.at<uchar>(row, col) = 200;
}*/
each_horizontal_len = 1;//线段长度计数
/开始向右检测
tmp_base_x = col + 1;
tmp_base_y = (tmp_beg_record_yiu + tmp_beg_record_yid) / 2;
startdetect:
perpendicular_up_line_len = 0;//检测up
tmp_up_yiu = 0;
tmp_up_yid = 0;
detect_up_line(tmp_base_x, tmp_base_y, binImg, perpendicular_up_line_len, tmp_up_yiu, tmp_up_yid);
perpendicular_down_line_len = 0;//检测down
tmp_down_yiu = height;
tmp_down_yid = height;
detect_down_line(tmp_base_x, tmp_base_y, binImg, perpendicular_down_line_len, tmp_down_yiu, tmp_down_yid, height);
if (perpendicular_down_line_len == 0 && perpendicular_up_line_len == 0){
detect_each_horizontal_len[cnt_perpendicular_line] = each_horizontal_len;
goto end;
}
if (perpendicular_down_line_len != 0 && perpendicular_up_line_len == 0){ //下方线段不为0
if ((tmp_down_yiu - tmp_base_y) > Q){
detect_each_horizontal_len[cnt_perpendicular_line] = each_horizontal_len;
goto end;
}
else if ((tmp_down_yiu - tmp_base_y <= Q) && (tmp_down_yid - tmp_down_yiu <= P)){
each_horizontal_len++;
record_detect_array[cnt_perpendicular_line][cnt_segment_line][0] = tmp_base_x;
record_detect_array[cnt_perpendicular_line][cnt_segment_line][1] = tmp_down_yiu;
record_detect_array[cnt_perpendicular_line][cnt_segment_line][2] = tmp_down_yid;
detect_each_horizontal_len[cnt_perpendicular_line] = each_horizontal_len;
cnt_segment_line++;
/*for (int tmp_hue_change = tmp_down_yiu; tmp_hue_change <= tmp_down_yid; tmp_hue_change++){
binImg.at<uchar>(tmp_hue_change, tmp_base_x) = 200;
}*/
tmp_base_x = tmp_base_x + 1;
tmp_base_y = (tmp_down_yiu + tmp_down_yid) / 2;
goto startdetect;
}
else if ((tmp_down_yiu - tmp_base_y <= Q) && (tmp_down_yid - tmp_down_yiu > P)){
each_horizontal_len++;
record_detect_array[cnt_perpendicular_line][cnt_segment_line][0] = tmp_base_x;
record_detect_array[cnt_perpendicular_line][cnt_segment_line][1] = record_detect_array[cnt_perpendicular_line][cnt_segment_line-1][1];
record_detect_array[cnt_perpendicular_line][cnt_segment_line][2] = record_detect_array[cnt_perpendicular_line][cnt_segment_line-1][2];
detect_each_horizontal_len[cnt_perpendicular_line] = each_horizontal_len;
cnt_segment_line++;
/*for (int tmp_hue_change = record_detect_array[cnt_perpendicular_line][1][1]; tmp_hue_change <= record_detect_array[cnt_perpendicular_line][1][2]; tmp_hue_change++){
binImg.at<uchar>(tmp_hue_change, tmp_base_x) = 200;
}*/
tmp_base_x = tmp_base_x + 1;
goto startdetect;
}
}
if (perpendicular_up_line_len != 0 && perpendicular_down_line_len == 0){ //上方线段不为0
if ((tmp_base_y - tmp_up_yid) > Q){
detect_each_horizontal_len[cnt_perpendicular_line] = each_horizontal_len;
goto end;
}
else if ((tmp_base_y - tmp_up_yid <= Q) && (tmp_up_yid - tmp_up_yiu <= P)){
each_horizontal_len++;
record_detect_array[cnt_perpendicular_line][cnt_segment_line][0] = tmp_base_x;
record_detect_array[cnt_perpendicular_line][cnt_segment_line][1] = tmp_up_yiu;
record_detect_array[cnt_perpendicular_line][cnt_segment_line][2] = tmp_up_yid;
detect_each_horizontal_len[cnt_perpendicular_line] = each_horizontal_len;
cnt_segment_line++;
/*for (int tmp_hue_change = tmp_up_yiu; tmp_hue_change <= tmp_up_yid; tmp_hue_change++){
binImg.at<uchar>(tmp_hue_change, tmp_base_x) = 200;
}*/
tmp_base_x = tmp_base_x + 1;
tmp_base_y = (tmp_up_yiu + tmp_up_yid) / 2;
goto startdetect;
}
else if ((tmp_base_y - tmp_up_yid <= Q) && (tmp_up_yid - tmp_up_yiu > P)){
each_horizontal_len++;
record_detect_array[cnt_perpendicular_line][cnt_segment_line][0] = tmp_base_x;
record_detect_array[cnt_perpendicular_line][cnt_segment_line][1] = record_detect_array[cnt_perpendicular_line][cnt_segment_line - 1][1];
record_detect_array[cnt_perpendicular_line][cnt_segment_line][2] = record_detect_array[cnt_perpendicular_line][cnt_segment_line - 1][2];
detect_each_horizontal_len[cnt_perpendicular_line] = each_horizontal_len;
cnt_segment_line++;
/*for (int tmp_hue_change = record_detect_array[cnt_perpendicular_line][1][1]; tmp_hue_change <= record_detect_array[cnt_perpendicular_line][1][2]; tmp_hue_change++){
binImg.at<uchar>(tmp_hue_change, tmp_base_x) = 200;
}*/
tmp_base_x = tmp_base_x + 1;
goto startdetect;
}
}
if (perpendicular_down_line_len != 0 && perpendicular_up_line_len != 0){ //均不为0
if ((tmp_down_yiu - tmp_base_y > Q) && (tmp_base_y - tmp_up_yid > Q)){
detect_each_horizontal_len[cnt_perpendicular_line] = each_horizontal_len;
goto end;
}
else if ((tmp_down_yiu - tmp_base_y > Q) && (tmp_base_y - tmp_up_yid <= Q)){//下方离太远,上方正常
if ((tmp_base_y - tmp_up_yid <= Q) && (tmp_up_yid - tmp_up_yiu <= P)){
each_horizontal_len++;
record_detect_array[cnt_perpendicular_line][cnt_segment_line][0] = tmp_base_x;
record_detect_array[cnt_perpendicular_line][cnt_segment_line][1] = tmp_up_yiu;
record_detect_array[cnt_perpendicular_line][cnt_segment_line][2] = tmp_up_yid;
detect_each_horizontal_len[cnt_perpendicular_line] = each_horizontal_len;
cnt_segment_line++;
/*for (int tmp_hue_change = tmp_up_yiu; tmp_hue_change <= tmp_up_yid; tmp_hue_change++){
binImg.at<uchar>(tmp_hue_change, tmp_base_x) = 200;
}*/
tmp_base_x = tmp_base_x + 1;
tmp_base_y = (tmp_up_yiu + tmp_up_yid) / 2;
goto startdetect;
}
else if ((tmp_base_y - tmp_up_yid <= Q) && (tmp_up_yid - tmp_up_yiu > P)){
each_horizontal_len++;
record_detect_array[cnt_perpendicular_line][cnt_segment_line][0] = tmp_base_x;
record_detect_array[cnt_perpendicular_line][cnt_segment_line][1] = record_detect_array[cnt_perpendicular_line][cnt_segment_line - 1][1];
record_detect_array[cnt_perpendicular_line][cnt_segment_line][2] = record_detect_array[cnt_perpendicular_line][cnt_segment_line - 1][2];
detect_each_horizontal_len[cnt_perpendicular_line] = each_horizontal_len;
cnt_segment_line++;
/*for (int tmp_hue_change = record_detect_array[cnt_perpendicular_line][1][1]; tmp_hue_change <= record_detect_array[cnt_perpendicular_line][1][2]; tmp_hue_change++){
binImg.at<uchar>(tmp_hue_change, tmp_base_x) = 200;
}*/
tmp_base_x = tmp_base_x + 1;
goto startdetect;
}
}
else if ((tmp_down_yiu - tmp_base_y <= Q) && (tmp_base_y - tmp_up_yid > Q)){//上方离太远,下方正常
if ((tmp_down_yiu - tmp_base_y <= Q) && (tmp_down_yid - tmp_down_yiu <= P)){
each_horizontal_len++;
record_detect_array[cnt_perpendicular_line][cnt_segment_line][0] = tmp_base_x;
record_detect_array[cnt_perpendicular_line][cnt_segment_line][1] = tmp_down_yiu;
record_detect_array[cnt_perpendicular_line][cnt_segment_line][2] = tmp_down_yid;
detect_each_horizontal_len[cnt_perpendicular_line] = each_horizontal_len;
cnt_segment_line++;
/*for (int tmp_hue_change = tmp_down_yiu; tmp_hue_change <= tmp_down_yid; tmp_hue_change++){
binImg.at<uchar>(tmp_hue_change, tmp_base_x) = 200;
}*/
tmp_base_x = tmp_base_x + 1;
tmp_base_y = (tmp_down_yiu + tmp_down_yid) / 2;
goto startdetect;
}
else if ((tmp_down_yiu - tmp_base_y <= Q) && (tmp_down_yid - tmp_down_yiu > P)){
each_horizontal_len++;
record_detect_array[cnt_perpendicular_line][cnt_segment_line][0] = tmp_base_x;
record_detect_array[cnt_perpendicular_line][cnt_segment_line][1] = record_detect_array[cnt_perpendicular_line][cnt_segment_line - 1][1];
record_detect_array[cnt_perpendicular_line][cnt_segment_line][2] = record_detect_array[cnt_perpendicular_line][cnt_segment_line - 1][2];
detect_each_horizontal_len[cnt_perpendicular_line] = each_horizontal_len;
cnt_segment_line++;
/*for (int tmp_hue_change = record_detect_array[cnt_perpendicular_line][1][1]; tmp_hue_change <= record_detect_array[cnt_perpendicular_line][1][2]; tmp_hue_change++){
binImg.at<uchar>(tmp_hue_change, tmp_base_x) = 200;
}*/
tmp_base_x = tmp_base_x + 1;
goto startdetect;
}
}
else if (((tmp_down_yiu - tmp_base_y <= Q) && (tmp_down_yid - tmp_down_yiu > P)) || ((tmp_base_y - tmp_up_yid <= Q) && (tmp_up_yid - tmp_up_yiu > P))){
each_horizontal_len++;
record_detect_array[cnt_perpendicular_line][cnt_segment_line][0] = tmp_base_x;
record_detect_array[cnt_perpendicular_line][cnt_segment_line][1] = record_detect_array[cnt_perpendicular_line][cnt_segment_line - 1][1];
record_detect_array[cnt_perpendicular_line][cnt_segment_line][2] = record_detect_array[cnt_perpendicular_line][cnt_segment_line - 1][2];
detect_each_horizontal_len[cnt_perpendicular_line] = each_horizontal_len;
cnt_segment_line++;
/*for (int tmp_hue_change = record_detect_array[cnt_perpendicular_line][1][1]; tmp_hue_change <= record_detect_array[cnt_perpendicular_line][1][2]; tmp_hue_change++){
binImg.at<uchar>(tmp_hue_change, tmp_base_x) = 200;
}*/
tmp_base_x = tmp_base_x + 1;
goto startdetect;
}
else if (((tmp_down_yiu - tmp_base_y <= Q) && (tmp_down_yid - tmp_down_yiu <= P)) && ((tmp_base_y - tmp_up_yid <= Q) && (tmp_up_yid - tmp_up_yiu <= P))){
if (tmp_down_yid - tmp_down_yiu + tmp_up_yid - tmp_up_yiu <= P){
each_horizontal_len++;
record_detect_array[cnt_perpendicular_line][cnt_segment_line][0] = tmp_base_x;
record_detect_array[cnt_perpendicular_line][cnt_segment_line][1] = tmp_up_yiu;
record_detect_array[cnt_perpendicular_line][cnt_segment_line][2] = tmp_down_yid;
detect_each_horizontal_len[cnt_perpendicular_line] = each_horizontal_len;
cnt_segment_line++;
for (int tmp_hue_change = tmp_up_yiu; tmp_hue_change <= tmp_down_yid; tmp_hue_change++){
binImg.at<uchar>(tmp_hue_change, tmp_base_x) = 200;
}
tmp_base_x = tmp_base_x + 1;
tmp_base_y = (tmp_up_yiu + tmp_down_yid) / 2;
goto startdetect;
}
else if (tmp_down_yid - tmp_down_yiu + tmp_up_yid - tmp_up_yiu > P){
each_horizontal_len++;
record_detect_array[cnt_perpendicular_line][cnt_segment_line][0] = tmp_base_x;
record_detect_array[cnt_perpendicular_line][cnt_segment_line][1] = record_detect_array[cnt_perpendicular_line][cnt_segment_line - 1][1];
record_detect_array[cnt_perpendicular_line][cnt_segment_line][2] = record_detect_array[cnt_perpendicular_line][cnt_segment_line - 1][2];
detect_each_horizontal_len[cnt_perpendicular_line] = each_horizontal_len;
cnt_segment_line++;
/*for (int tmp_hue_change = record_detect_array[cnt_perpendicular_line][1][1]; tmp_hue_change <= record_detect_array[cnt_perpendicular_line][1][2]; tmp_hue_change++){
binImg.at<uchar>(tmp_hue_change, tmp_base_x) = 200;
}*/
tmp_base_x = tmp_base_x + 1;
goto startdetect;
}
}
}
}
detect_per_flag = 0;
end:
if (each_horizontal_len > 1500){//如果足够长,阈值
cnt_perpendicular_line++;
}
else{ //清空
for (int cnt_out = 0; cnt_out < each_horizontal_len; cnt_out++){
for (int cnt_in = 0; cnt_in < 3; cnt_in++){
record_detect_array[cnt_perpendicular_line][cnt_out][cnt_in] = 0;
}
}
detect_each_horizontal_len[cnt_perpendicular_line] = 0;
}
}
}
}
实验效果
虽然代码中用了C++中不建议使用的goto语句,但检测效果鲁棒性相对Hough变换要强。加上Rect分割并进行图像校正后效果如图。