在简单的黑白绘图图像中检测直线,圆和圆弧

时间:2013-11-11 17:37:23

标签: c++ algorithm qt opencv image-processing

我试图在一个简单的黑白图纸图像文件(jpg或bmp格式)中检测线条,圆圈和圆弧

我之前发布过similar question,其中建议使用OpenCV库。这是一个很好的图书馆,然而,它不够准确,不适合我的目的。更具体地说,Canny检测算法在某种程度上对我的图像不起作用。

因此我试图使用QImage自己实现该算法。我已成功地将其用于直线。 Qt C ++中的代码如下。这是一个非常混乱的代码,但我只是给它参考。

算法非常简单:
我从左上角开始扫描图像 2.每当遇到黑色像素时,我向右扫描,向左扫描,检查它是否是线段的一角。

for ( int i = 0; i < myImage.height(); i++ ) {

        for ( int j = 0; j < myImage.width(); j++ ) {
            if ( qGray( myImage.pixel( j, i ) ) == 0 ) {

                myImage.setPixel( j, i, value );
                bool horiLineDrawn = false;
                int xRight = j+1, xLeft = j-1;
                int y = i+1;
                while ( xRight < myImage.width() && qGray( myImage.pixel( xRight, i ) ) == 0 ) {
                    myImage.setPixel( xRight, i, value );
                    xRight++;
                }
                while ( y < myImage.height() && xLeft >= 0 &&
                        qGray( myImage.pixel( xLeft, y ) ) == 0 ) {
                    if ( xLeft - 1 >= 0 &&
                         qGray( myImage.pixel( xLeft - 1, y ) ) == 0 ) {
                        while ( xLeft >= 0 &&
                                qGray( myImage.pixel( xLeft, y ) ) == 0 ) {
                            myImage.setPixel( xLeft, y, value );
                            xLeft--;
                        }
                        y++;
                    } else if ( y+1 < myImage.height() &&
                                qGray( myImage.pixel( xLeft, y + 1 ) ) == 0 ) {
                        while ( y < myImage.height() &&
                                qGray( myImage.pixel( xLeft, y ) ) == 0 ) {
                            myImage.setPixel( xLeft, y, value );
                            y++;
                        }
                        xLeft--;
                    } else {
                        xLeft--;
                        y++;
                    }
                }
                y--;
                xLeft++;
                if ( y > i && ( y - i > MIN_PIXELS_LINE ||
                                xRight-1 - xLeft > MIN_PIXELS_LINE )
                     ) {
                    drawFile.Line( fileName2, xRight-1, myImage.height() - i, xLeft,
                                   myImage.height() - y, 0 );
                    horiLineDrawn = true;
                }

                y = i + 1;
                while ( y < myImage.height() && xRight < myImage.width() &&
                        qGray( myImage.pixel( xRight, y ) ) == 0 ) {


                    if ( xRight + 1 < myImage.width() &&
                         qGray( myImage.pixel( xRight + 1, y ) ) == 0 ) {
                        while ( xRight < myImage.width() &&
                                qGray( myImage.pixel( xRight, y ) ) == 0 ) {
                            myImage.setPixel( xRight, y, value );
                            xRight++;
                        }
                        y++;
                    } else if ( y+1 < myImage.height() &&
                                qGray( myImage.pixel( xRight, y + 1 ) ) == 0 ) {
                        while ( y < myImage.height() &&
                                qGray( myImage.pixel( xRight, y ) ) == 0 ) {
                            myImage.setPixel( xRight, y, value );
                            y++;
                        }
                        xRight++;
                    } else {
                        xRight++;
                        y++;
                    }
                }
                y--;
                xRight--;
                if ( y - i > MIN_PIXELS_LINE || xRight - j > MIN_PIXELS_LINE
                     && !horiLineDrawn) {
                    drawFile.Line( fileName2, j, myImage.height() - i, xRight,
                                   myImage.height() - y, 0 );
                    horiLineDrawn = true;
                }

                y = i + 1;
                while ( y < myImage.height() && qGray( myImage.pixel( j, y ) ) == 0 ) {
                    myImage.setPixel( j, y, value );
                    y++;
                }
                xLeft = j - 1;
                xRight = j + 1;
                if ( xLeft >= 0 && y < myImage.height() &&
                     qGray( myImage.pixel( xLeft, y ) ) == 0 ) {
                    while ( xLeft >= 0 && y < myImage.height() &&
                            qGray( myImage.pixel( xLeft, y ) ) == 0 ) {
                        while ( y < myImage.height() &&
                                qGray( myImage.pixel( xLeft, y ) ) == 0 ) {
                            myImage.setPixel( xLeft, y, value );
                            y++;
                        }
                        xLeft--;
                    }
                    xLeft++;
                    y--;
                    if ( y - i > MIN_PIXELS_LINE || j - xLeft > MIN_PIXELS_LINE )
                        drawFile.Line( fileName2, j, myImage.height() - i, xLeft,
                                       myImage.height() - y, 0 );
                } else if ( xRight < myImage.width() && y < myImage.height() &&
                            qGray( myImage.pixel( xRight, y ) ) == 0 ) {
                       while ( xRight < myImage.width() && y < myImage.height() &&
                               qGray( myImage.pixel( xRight, y ) ) == 0 ) {
                           while ( y < myImage.height() &&
                                   qGray( myImage.pixel( xRight, y ) ) == 0 ) {
                               myImage.setPixel( xRight, y, value );
                               y++;
                           }
                           xRight++;
                       }
                       xRight--;
                       y--;
                       if ( y - i > MIN_PIXELS_LINE || xRight - j > MIN_PIXELS_LINE )
                           drawFile.Line( fileName2, j, myImage.height() - i, xRight,
                                          myImage.height() - y, 0 );
                } else {
                    y--;
                    if ( y - i > MIN_PIXELS_LINE )
                        drawFile.Line( fileName2, j, myImage.height() - i, j,
                                       myImage.height() - y, 0 );
                }


            }
        }
    }

这很好用。例如:

输入图像:
enter image description here

输出图像:
enter image description here

有谁能建议我如何为圆和弧实现类似或更好的算法?效率不是问题,因为我的图像大小应该是最大1000乘1000像素。 但是,准确性至关重要。

编辑:在我目前的直线实现中可能存在很多错误,比如我没有对相交线进行测试等。但我认为我将能够处理这些并发症。

1 个答案:

答案 0 :(得分:3)

出于好奇,你的所有图片都是用细线二进制的吗?这些扫描的手绘图或像素艺术?我问,因为你会遇到使用JPEG压缩的麻烦,这在线条艺术上是非常糟糕的。您应该确保始终使用带有线条图的无损压缩。

如果图像中存在噪声和其他瑕疵,任何边缘检测器都不太可能完美。如果我正在攻击这个问题,我会专注于预处理数据,使其具有更强的线条,以便线检测过程更容易。这可以通过预先对图像进行阈值处理,可能进行一些形态清理,甚至锐化图像来完成。

此外,如果您的图像已经是二进制(或者可以使用简单的阈值制作二进制),则Canny边缘检测(或任何灰度边缘检测器)可能不是最佳工具。您最好使用findContours之类的内容来制作图像二进制文件以识别边缘。

如果您正在寻找与Hough变换稍微不同的东西来识别形状,您可以尝试使用模型拟合算法,如RANSAC。