c++ - 在简单的黑白绘图图像中检测直线、圆和弧

标签 c++ algorithm qt opencv image-processing

我正在尝试检测简单的黑白绘图图像文件(jpg 或 bmp 格式)中的直线、圆和弧

我发布了一个similar question之前,建议使用 OpenCV 库。这是一个很好的图书馆,但是,对于我的目的来说,它不够准确。更具体地说,Canny 检测算法在某种程度上无法完美地处理我的图像。

因此我尝试使用 QImage 自己实现算法。我已经成功地为直线实现了它。 Qt C++中的代码如下。这是一个非常困惑的代码,但我只是提供它以供引用。

算法很简单:
1. 我从左上角逐行扫描图像。
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 x 1000 像素。 然而,准确性至关重要。

编辑:在我目前的直线实现中可能有很多错误,比如我还没有测试它的相交线等等。但我想我应该能够处理这些复杂情况。

最佳答案

出于好奇,你所有的图像都是二值化的细线吗?这些是扫描的手绘图还是像素画?我问是因为使用 JPEG 压缩会遇到麻烦,它在艺术线条上是出了名的糟糕。您应该确保始终对线条图使用无损压缩。

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

另外,如果您的图像已经是二进制的(或者可以通过简单的阈值变成二进制的),Canny 边缘检测(或者实际上任何灰度边缘检测器)可能不是最好的使用工具。您最好将图像设为二进制并使用类似 findContours 的东西识别边缘。

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

关于c++ - 在简单的黑白绘图图像中检测直线、圆和弧,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19912454/

相关文章:

c++ - 如何创建新桌面? C++

c++ - C++ 中的别名

python - 有计算利萨如图形面积的算法吗?

c++ - 实现伪多项式 DP 子集和

c++ - Qt音乐播放器: mini player mode

c++ - C++ 程序中的奇怪错误 : Removing Printout Breaks Program

c++ - 修改指向另一个指针的指针

java - 在图中构建随机哈密顿路径

java - Qt 占碑 : QAbstractListModel not displaying in QListView

c++ - 如何从静态 Qt 运行 qmake