SFML C ++ Canny边缘检测双边缘

时间:2018-08-22 21:57:02

标签: c++ image-processing sfml canny-operator sobel

因此,我决定像锻炼之前一样创建一个简单的Canny边缘检测器,然后再对图像处理进行更严格的讨论。

我尝试遵循Canny的典型路径:  1.灰度图像  2.高斯滤波器使噪声模糊  3.边缘检测-我同时使用Sobel和Scharr  4.边缘细化-我在取决于梯度方向的方向上使用了非最大抑制-垂直,水平,45个对角线或135个对角线  5.磁滞

我以某种方式设法使它与Scharr的检测一起工作,但是我经常遇到双边或多边的问题,尤其是Sobel。我真的找不到能使它工作的一组参数。

我的Sobel算法:

void sobel(sf::Image &image, pixldata **garray, float division)
{
int t1 = 0, t2 = 0, t3 = 0, t4 = 0;
sf::Color color;
sf::Image bufor;
bufor.create(image.getSize().x, image.getSize().y, sf::Color::Cyan);

for (int i = 1;i < image.getSize().y - 1;i++)
{
    for (int j = 1;j < image.getSize().x - 1;j++)
    {

        t1 = (- image.getPixel(j - 1, i - 1).r - 2 * image.getPixel(j - 1, i).r - image.getPixel(j - 1, i + 1).r + image.getPixel(j + 1, i - 1).r + 2 * image.getPixel(j + 1, i).r + image.getPixel(j + 1, i + 1).r) / division;
        t2 = (- image.getPixel(j - 1, i).r - 2 * image.getPixel(j - 1, i + 1).r - image.getPixel(j, i + 1).r + image.getPixel(j + 1, i).r + 2 * image.getPixel(j + 1, i - 1).r + image.getPixel(j, i - 1).r) / division;
        t3 = (- image.getPixel(j - 1, i + 1).r - 2 * image.getPixel(j, i + 1).r - image.getPixel(j + 1, i + 1).r + image.getPixel(j - 1, i - 1).r + 2 * image.getPixel(j, i - 1).r + image.getPixel(j + 1, i - 1).r) / division;
        t4 = (- image.getPixel(j, i + 1).r - 2 * image.getPixel(j + 1, i + 1).r - image.getPixel(j + 1, i).r + image.getPixel(j - 1, i).r + 2 * image.getPixel(j - 1, i - 1).r + image.getPixel(j, i - 1).r) / division;

        color.r = (abs(t1) + abs(t2) + abs(t3) + abs(t4));
        color.g = (abs(t1) + abs(t2) + abs(t3) + abs(t4));
        color.b = (abs(t1) + abs(t2) + abs(t3) + abs(t4));

        garray[j][i].gx = t1;
        garray[j][i].gy = t3;
        garray[j][i].gtrue = sqrt(t1*t1 + t2*t2 + t3*t3 + t4*t4);
        garray[j][i].gsimpl = sqrt(t1*t1 + t2*t2);

        t1 = abs(t1);
        t2 = abs(t2);
        t3 = abs(t3);
        t4 = abs(t4);

        if (t1 > t4 && t1 > t3 && t1 > t2)
            garray[j][i].fi = 0;
        else if (t2 > t4 && t2 > t3 && t2 > t1)
            garray[j][i].fi = 45;
        else if (t3 > t4 && t3 > t2 && t3 > t1)
            garray[j][i].fi = 90;
        else if (t4 > t3 && t4 > t2 && t4 > t1)
            garray[j][i].fi = 135;
        else
            garray[j][i].fi = 0;

        if (sqrt(t1*t1 + t2*t2 + t3*t3 + t4*t4) < 0)
        {
            color.r = 0;
            color.g = 0;
            color.b = 0;
        }
        else if (sqrt(t1*t1 + t2*t2 + t3*t3 + t4*t4) > 255)
        {
            color.r = 255;
            color.g = 255;
            color.b = 255;
        }
        else
        {
            color.r = sqrt(t1*t1 + t2*t2 + t3*t3 + t4*t4);
            color.g = sqrt(t1*t1 + t2*t2 + t3*t3 + t4*t4);
            color.b = sqrt(t1*t1 + t2*t2 + t3*t3 + t4*t4);
        }
        bufor.setPixel(j, i, color);
    }
}
image.copy(bufor, 0, 0);
}

Scharr的代码仅在将像素值相乘上有所不同。

        t1 = (-3 * image.getPixel(j - 1, i - 1).r - 10 * image.getPixel(j - 1, i).r - 3 * image.getPixel(j - 1, i + 1).r + 3 * image.getPixel(j + 1, i - 1).r + 10 * image.getPixel(j + 1, i).r + 3 * image.getPixel(j + 1, i + 1).r) / division;
        t2 = (-3 * image.getPixel(j - 1, i).r - 10 * image.getPixel(j - 1, i + 1).r - 3 * image.getPixel(j, i + 1).r + 3 * image.getPixel(j + 1, i).r + 10 * image.getPixel(j + 1, i - 1).r + 3 * image.getPixel(j, i - 1).r) / division;
        t3 = (-3 * image.getPixel(j - 1, i + 1).r - 10 * image.getPixel(j, i + 1).r - 3 * image.getPixel(j + 1, i + 1).r + 3 * image.getPixel(j - 1, i - 1).r + 10 * image.getPixel(j, i - 1).r + 3 * image.getPixel(j + 1, i - 1).r) / division;
        t4 = (-3 * image.getPixel(j, i + 1).r - 10 * image.getPixel(j + 1, i + 1).r - 3 * image.getPixel(j + 1, i).r + 3 * image.getPixel(j - 1, i).r + 10 * image.getPixel(j - 1, i - 1).r + 3 * image.getPixel(j, i - 1).r) / division;

细化代码:

void intelligentThin(sf::Image &image, int radius, pixldata **garray)
{
int xmax = image.getSize().x;
int ymax = image.getSize().y;
bool judgeandjury = true;

for (int i = 0;i < xmax;i++)
{
    int leftBound = 0, rightBound = 0, ceilBound = 0, bottomBound = 0;

    if (i < radius)
    {
        leftBound = 0;
        rightBound = i + radius;
    }
    else if (i >= xmax - radius)
    {
        leftBound = i - radius;
        rightBound = xmax - 1;
    }
    else
    {
        leftBound = i - radius;
        rightBound = i + radius;
    }

    for (int j = 0;j < ymax;j++)
    {
        if (j < radius)
        {
            ceilBound = 0;
            bottomBound = j + radius;
        }
        else if (j >= ymax - radius)
        {
            ceilBound = j - radius;
            bottomBound = ymax - 1;
        }
        else
        {
            ceilBound = j - radius;
            bottomBound = j + radius;
        }

        if (garray[i][j].fi == 0)
        {
            for (int t = leftBound; t <= rightBound; t++)
            {
                if ((image.getPixel(t, j).r >= image.getPixel(i, j).r) && (t != i))
                {
                    judgeandjury = false;
                }
            }
        }
        else if (garray[i][j].fi == 135)
        {
            for (int l = leftBound, t = ceilBound; (l <= rightBound && t <= bottomBound); l++, t++)
            {
                if ((image.getPixel(l, t).r >= image.getPixel(i, j).r) && (t != j))
                {
                    judgeandjury = false;
                }
            }
        }
        else if (garray[i][j].fi == 90)
        {
            for (int t = ceilBound; t <= bottomBound; t++)
            {
                if ((image.getPixel(i, t).r >= image.getPixel(i, j).r) && (t != j))
                {
                    judgeandjury = false;
                }
            }
        }
        else if (garray[i][j].fi == 45)
        {
            for (int l = rightBound, t = ceilBound; (l >= leftBound && t <= bottomBound); l--, t++)
            {
                if ((image.getPixel(l, t).r >= image.getPixel(i, j).r) && (t != j))
                {
                    judgeandjury = false;
                }
            }
        }

        if (judgeandjury == false)
        {
            image.setPixel(i, j, sf::Color::Black);
        }

        judgeandjury = true;

    }
    leftBound = rightBound = 0;
}
}

磁滞代码:

void hysteresis(sf::Image &image, int radius, int uplevel, int lowlevel)
{

int xmax = image.getSize().x;
int ymax = image.getSize().y;
bool judgeandjury = false;

sf::Image bufor;
bufor.create(image.getSize().x, image.getSize().y, sf::Color::Cyan);

for (int i = 0;i < xmax;i++)
{
    int leftBound = 0, rightBound = 0, ceilBound = 0, bottomBound = 0;

    if (i < radius)
    {
        leftBound = 0;
        rightBound = i + radius;
    }
    else if (i >= xmax - radius)
    {
        leftBound = i - radius;
        rightBound = xmax - 1;
    }
    else
    {
        leftBound = i - radius;
        rightBound = i + radius;
    }

    for (int j = 0;j < ymax;j++)
    {
        int currentPoint = image.getPixel(i, j).r;

        if (j < radius)
        {
            ceilBound = 0;
            bottomBound = j + radius;
        }
        else if (j >= ymax - radius)
        {
            ceilBound = j - radius;
            bottomBound = ymax - 1;
        }
        else
        {
            ceilBound = j - radius;
            bottomBound = j + radius;
        }

        if (currentPoint > uplevel)
        {
            judgeandjury = true;
        }
        else if (currentPoint > lowlevel)
        {
            for (int t = leftBound; t <= rightBound; t++)
            {
                for (int l = ceilBound; l <= bottomBound; l++)
                {
                    if (image.getPixel(t, l).r > uplevel)
                    {
                        judgeandjury = true;
                    }

                }
            }
        }
        else judgeandjury = false;

        if (judgeandjury == true)
        {
            bufor.setPixel(i, j, sf::Color::White);
        }
        else
        {
            bufor.setPixel(i, j, sf::Color::Black);
        }

        judgeandjury = false;
        currentPoint = 0;

    }
    leftBound = rightBound = 0;
}
image.copy(bufor, 0, 0);
}

对于Sobel来说,结果是非常不满意的:

Thinning the Sobel

Sobel after hysteresis

使用Scharr,结果会更好:

Thinned Scharr

Scharr after hysteresis

一组参数:

#define thinsize 1                  
#define scharrDivision 1        
#define sobelDivision 1                 
#define hysteresisRadius 1          
#define level 40                    
#define hysteresisUpperLevelSobel 80        
#define hysteresisLowerLevelSobel 60        
#define hysteresisUpperLevelScharr 200      
#define hysteresisLowerLevelScharr 100      

如您所见,Sobel存在一个问题,它会产生双重边缘。沙尔也会产生一些噪音,但我认为这是可以接受的。当然,如果有人可以提供一些建议,它总是会变得更好:)

此行为的原因是什么?是我的错误或算法不佳导致的,还是只是参数的一种情况?

编辑: 发布main()

sf::Image imydz;
imydz.loadFromFile("lena.jpg");
int x = imydz.getSize().x;
int y = imydz.getSize().y;


pixldata **garray = new pixldata *[x];
for (int i = 0;i < x;i++)
{
garray[i] = new pixldata[y];
}


monochrome(imydz);
gauss(imydz, radius, sigma);

//sobel(imydz, garray, sobelDivision);

scharr(imydz, garray, scharrDivision);

intelligentThin(imydz, thinsize, garray);
hysteresis(imydz, hysteresisRadius, hysteresisUpperLevel, hysteresisLowerLevel);

第二次编辑-修复了抑制:

sf::Image bufor;
bufor.create(image.getSize().x, image.getSize().y, sf::Color::Black);
for (int i = 1;i < xmax - 1;i++)
{
    for (int j = 1;j < ymax - 1;j++)
    {
        if (garray[i][j].fi == 0)
        {
            if (((image.getPixel(i, j).r >= image.getPixel(i + 1, j).r) && (image.getPixel(i, j).r > image.getPixel(i - 1, j).r)) ||
                ((image.getPixel(i, j).r > image.getPixel(i + 1, j).r) && (image.getPixel(i, j).r >= image.getPixel(i - 1, j).r)))
            {
                judgeandjury = true;
            }
            else judgeandjury = false;
        }
...
 if (judgeandjury == false)
        {
            bufor.setPixel(i, j, sf::Color::Black);
        }
        else bufor.setPixel(i, j, image.getPixel(i, j));
        judgeandjury = false;
    }
}
image.copy(bufor, 0, 0);

Repaired Scharr on Lena 好像很奇怪 Another test image - strange results

Before binarization

Ready gears

1 个答案:

答案 0 :(得分:0)

我没有详细阅读整个代码,那里的代码太多了。但是很明显,您的非最大抑制代码是错误的。让我们看一下它对图像中间一个像素(渐变接近0度)的作用:

leftBound = i - radius;
rightBound = i + radius;
// ...
for (int t = leftBound; t <= rightBound; t++)
{
   if ((image.getPixel(t, j).r >= image.getPixel(i, j).r) && (t != i))
   {
      judgeandjury = false; // it's not a maximum: suppress
   }
}
// ...
if (judgeandjury == false)
{
   image.setPixel(i, j, sf::Color::Black);
}

在这里,radius被调用代码设置为1。任何其他值都将是错误的,因此可以。我将其完全删除为参数。现在您的循环是:

for (int t = i-1; t <= t+1; t++)
   if (t != i)

这意味着您恰好命中了t的两个值。因此,这当然应该替换为更简单的,不会循环的代码,它将更具可读性。

这就是现在所做的:

if (   (image.getPixel(i-1, j).r >= image.getPixel(i, j).r)
    || (image.getPixel(i+1, j).r >= image.getPixel(i, j).r)) {
   judgeandjury = false; // it's not a maximum: suppress
}

因此,如果像素不严格大于其相邻像素,则可以抑制该像素。回顾Wikipedia article,似乎他们建议相同。但是实际上,这是不正确的,您希望该点严格大于两个邻居中的一个,并且大于或等于另一个。这防止了在两个相邻像素上碰巧梯度同样强的情况。实际最大值可以恰好落在两个像素的中间,从而在该局部最大梯度上产生两个像素,它们的值完全相同。但是让我们暂时忽略这种情况,有可能但并非全部。

接下来,您在输入图像中隐藏最大... !这意味着,当您到达该行的下一个像素时,您将其值与刚刚被抑制的该值进行比较。当然,它会更大,即使它小于该位置的原始值。也就是说,非最大值看起来像最大值,因为您将相邻像素设置为0。

因此:将算法结果写入输出图像:

if (judgeandjury == true)
{
   output.setPixel(i, j, image.getPixel(i, j));
}

...您当然需要分配哪个,但是您已经知道了。


您的第二个问题是在sobel函数中,您可以在其中计算梯度大小。它将裁剪(饱和)输出。通过将输出的值切割为255到255以上,可以沿着恒定值的边缘创建非常宽的线。非最大抑制的测试在这条线的两个边缘都可以满足,但在中间的像素(两个像素的相邻像素具有相同的值)中不能满足。

要解决此问题,请:

  1. 使用浮点缓冲区存储梯度幅度。在这里,您无需担心数据范围。

  2. 将幅度除以某个值,以使其永远不会超过255。现在,您要量化幅度而不是裁剪幅度。在这种情况下,量化 应该没问题。

我强烈建议您遵循(1)。我通常使用浮点数对所有图像都使用值,并且仅将其转换为8位整数进行显示。这简化了很多事情!