如何有效地计算平均"方向"在灰度图像的像素? [英] How to efficiently compute the average "direction" of pixels in a grayscale image?

查看:98
本文介绍了如何有效地计算平均"方向"在灰度图像的像素?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

所以我想通了,我可以将图像转换为灰度是这样的:

 公共静态位图灰度(此图片IMG) 
{
变种BMP =新位图(img.Width,img.Height);使用
(VAR G = Graphics.FromImage(BMP))
{
变种嘉洛斯=新嘉洛斯(
新的[]
{
新[] {.30f,.30f,.30f,0,0},
新的[] {.59f,.59f,.59f,0,0},
新的[] {.11f,.11f ,.11f,0,0},
新[] {0,0,0,1.0F,0},
新[] {0,0,0,0,1.0F}
});

使用(VAR ATTRS =新ImageAttributes())
{
attrs.SetColorMatrix(嘉洛斯);
g.DrawImage(IMG,新的Rectangle(0,0,img.Width,img.Height),
0,0,img.Width,img.Height,GraphicsUnit.Pixel,ATTRS);
}
}
返回BMP;
}

现在,我要计算像素的平均值的方向发展。



我的意思是,我想看看说一个3x3的区域,然后如果左侧比右侧暗,则方向将是右,如果底部比顶部较深,则方向将是向上,如果左下比右上方较暗,则方向将是向上的权利。 (想想小矢量箭头超过每3x3的区域)。也许一个更好的例子是,如果你在Photoshop绘制一个灰度渐变,并要计算在他们画什么角度了。



我做过这样的东西MATLAB,但那是几年前。我想我可以用类似于嘉洛斯来计算这个矩阵,但我不太清楚如何。它看起来像此功能可能是我想要的东西;可能我将其转换为灰度图像(如上),然后做一些与灰度矩阵来计算这些方向?



IIRC,我要的是非常相似的边缘检测



在我计算这些方向向量,我只是去遍历他们和计算图像的平均方向。



的最终目标是我要旋转图像,这样他们的平均方向始终向上;这样,如果我有两个相同的图像除了一个旋转(90,180或270度),他们最终将面向以同样的方式(我不关心,如果一个人结束了颠倒)。






*剪断* 删除一些垃圾邮件。你想读我尝试的其余部分,你可以查看修改。


解决方案

计算角度的平均值通常是一个坏思路:

  ... 
总和+ = Math.Atan2(彝族,十一)。
}
}
双平均=总和/(img.Width * img.Height);



一组角度的平均值并没有明确的意义:例如,一个角的平均指点和一个角度朝下指向右侧一个角度。那是你要的吗?假设向上是+ PI,然后两个角度之间的平均几乎的指向上方将是一个角度朝下,如果一个角度的PI [一些小值],其他-PI +些小值]。这可能不是你想要的。此外,你完全无视边缘的强度 - 最真实的生活图像的像素不是边缘可言,所以梯度方向主要是噪音。



如果你要计算像一个平均方向的东西,你需要添加上去的向量的替代角度,再经过计算ATAN2循环。问题是:矢量和告诉你什么有关图像中的对象,如指向相反方向的梯度相互抵消。它只是告诉你一些有关第一/最后一排和图像的第一个/最后一列之间的亮度差异。 。这可能不是你想要的。



我觉得定向图像最简单的方法是创建一个角度柱状图:创建(EG)的数组360箱360°的梯度方向。然后计算每个像素的梯度角和幅度。每个梯度幅度添加到合适的角度斌。这不会给你一个单一的角度,但角直方图,然后可将其用于定向两个图像彼此使用简单的循环相关



下面是一个验证的概念落实数学我扔在一起,看看是否这会工作:

  angleHistogram [src_]:= 

Lx的= GaussianFilter [的ImageData [SRC],2,{0,1}];
阮文黎= GaussianFilter [的ImageData [SRC],2,{1,0}];
angleAndOrientation =
MapThread [{回合[ARCTAN [#1,#2] * 180 / \ [PI],
的Sqrt [#1 ^ 2 +#2 ^ 2]}&放大器; {Lx的,LY} 2];
angleAndOrientationFlat =拼合[angleAndOrientation,1];
仓= BinLists [angleAndOrientationFlat,1,5];
直方图=
共计/ @拼合[仓[[全部,全部,全部,2]],{{1},{2,3}}];
maxIndex =位置[直方图,最大[直方图]] [[1,1] ];
标记为[
显示[
ListLinePlot [直方图,PlotRange - >全部],
图形[{红,点[{maxIndex,直方图[maxIndex]} ]}]
]最大的<>的toString [maxIndex<> \ [等级]]



与示例图像结果:





角度柱状图也显示了为什么平均角度不能工作:直方图实质上是一个尖峰,其他的角度大致一致。该直方图的平均总是会被制服的背景噪音为主。这就是为什么你得几乎每个与当前的算法中的活生生的图像相同的角度(约180°)。



树图像有一个单一的支配角度(地平线),所以在这种情况下,可以使用柱状图(最频繁的角)的方式。但是,这不会为每个图像的工作:





在这里你有两个峰。循环相关仍应定位两个图像彼此,而是简单地使用模式可能是不足够的。



另外请注意,在角度柱状图的峰值是不向上 :在上面的树形象,在角度直方图的峰值可能是在地平线上。所以它朝上。在莉娜的形象,它在后台垂直白条 - 因此它指向右侧。只需定向使用最频繁的夹角的的又与右侧朝上每个图像的图像





这形象更加具有峰:使用模式(或,也许任何一个角度)将是不可靠的定向此图像。但角度直方图作为一个整体应该还是给你一个可靠的方向



注意:我没有预先处理图像,我没有尝试在不同尺度梯度算,我没有后处理所产生的直方图。在实际应用中,你会调整所有这些东西,以获得最好的算法大集测试图像。这只是一个简单的测试,看看这个想法可以在所有的工作。



地址:使用此直方图,你会以东方两个图像



<醇>
  • 正常化所有直方图,所以直方图下的面积是每个图像(即使有的更亮,更暗或模糊)相同

  • 拍摄的图像的直方图,并比较它们对你感兴趣的每次旋转:



  • 举例来说,在C#中:

     的for(int rotationAngle = 0; rotationAngle< 360; rotationAngle ++)
    {
    INT差= 0;
    的for(int i = 0; I< 360;我++)
    差异+ = Math.Abs​​(histogram1 [I] - histogram2 [(1 + rotationAngle)360%]);
    如果(差值< bestDifferenceSoFar)
    {
    bestDifferenceSoFar =差;
    foundRotation = rotationAngle;
    }
    }



    (你可以加快这,如果你的直方图使用FFT长度是二的幂,但该代码将是一个复杂得多,并且为256箱,它可能那么重要)


    So I figured out I can convert an image to grayscale like this:

    public static Bitmap GrayScale(this Image img)
    {
        var bmp = new Bitmap(img.Width, img.Height);
        using(var g = Graphics.FromImage(bmp))
        {
            var colorMatrix = new ColorMatrix(
                new[]
                    {
                        new[] {.30f, .30f, .30f, 0, 0},
                        new[] {.59f, .59f, .59f, 0, 0},
                        new[] {.11f, .11f, .11f, 0, 0},
                        new[] {0, 0, 0, 1.0f, 0},
                        new[] {0, 0, 0, 0, 1.0f}
                    });
    
            using(var attrs = new ImageAttributes())
            {
                attrs.SetColorMatrix(colorMatrix);
                g.DrawImage(img, new Rectangle(0, 0, img.Width, img.Height),
                    0, 0, img.Width, img.Height, GraphicsUnit.Pixel, attrs);
            }
        }
        return bmp;
    }
    

    Now, I want to compute the average "direction" of the pixels.

    What I mean by that is that I want to look at say a 3x3 region, and then if the left side is darker than the right side, then the direction would be to the right, if the bottom is darker than the top, then the direction would be upwards, if the bottom-left is darker than the top-right, then the direction would be up-right. (Think of little vector arrows over every 3x3 region). Perhaps a better example is if you draw a grayscale gradient in photoshop, and you want to compute at what angle they drew it.

    I've done stuff like this MatLab, but that was years ago. I figure I could use a matrix similar to ColorMatrix to compute this, but I'm not quite sure how. It looks like this function might be what I want; could I convert it to grayscale (as above) and then do something with the grayscale matrix to compute these directions?

    IIRC, what I want is quite similar to edge detection.

    After I compute these direction vectors, I'm just going to loop over them and compute the average direction of the image.

    The end goal is I want to rotate images so that their average direction is always upwards; this way if I have two identical images except one is rotated (90,180 or 270 degrees), they will end up oriented the same way (I'm not concerned if a person ends up upside down).


    *snip* Deleting some spam. You can view the revisions of you want to read the rest of my attempts.

    解决方案

    Calculating the mean of angles is generally a bad idea:

    ...
            sum += Math.Atan2(yi, xi);
        }
    }
    double avg = sum / (img.Width * img.Height);
    

    The mean of a set of angles has no clear meaning: For example, the mean of one angle pointing up and one angle pointing down is a angle pointing right. Is that what you want? Assuming "up" is +PI, then the mean between two angles almost pointing up would be an angle pointing down, if one angle is PI-[some small value], the other -PI+[some small value]. That's probably not what you want. Also, you're completely ignoring the strength of the edge - most of the pixels in your real-life images aren't edges at all, so the gradient direction is mostly noise.

    If you want to calculate something like an "average direction", you need to add up vectors instead of angles, then calculate Atan2 after the loop. Problem is: That vector sum tells you nothing about objects inside the image, as gradients pointing in opposite directions cancel each other out. It only tells you something about the difference in brightness between the first/last row and first/last column of the image. That's probably not what you want.

    I think the simplest way to orient images is to create an angle histogram: Create an array with (e.g.) 360 bins for 360° of gradient directions. Then calculate the gradient angle and magnitude for each pixel. Add each gradient magnitude to the right angle-bin. This won't give you a single angle, but an angle-histogram, which can then be used to orient two images to each other using simple cyclic correlation.

    Here's a proof-of-concept Mathematica implementation I've thrown together to see if this would work:

    angleHistogram[src_] :=
     (
      Lx = GaussianFilter[ImageData[src], 2, {0, 1}];
      Ly = GaussianFilter[ImageData[src], 2, {1, 0}];
      angleAndOrientation = 
       MapThread[{Round[ArcTan[#1, #2]*180/\[Pi]], 
          Sqrt[#1^2 + #2^2]} &, {Lx, Ly}, 2];
      angleAndOrientationFlat = Flatten[angleAndOrientation, 1];
      bins = BinLists[angleAndOrientationFlat , 1, 5];
      histogram = 
       Total /@ Flatten[bins[[All, All, All, 2]], {{1}, {2, 3}}];
      maxIndex = Position[histogram, Max[histogram]][[1, 1]];
      Labeled[
       Show[
        ListLinePlot[histogram, PlotRange -> All],
        Graphics[{Red, Point[{maxIndex, histogram[[maxIndex]]}]}]
        ], "Maximum at " <> ToString[maxIndex] <> "\[Degree]"]
      )
    

    Results with sample images:

    The angle histograms also show why the mean angle can't work: The histogram is essentially a single sharp peak, the other angles are roughly uniform. The mean of this histogram will always be dominated by the uniform "background noise". That's why you've got almost the same angle (about 180°) for each of the "real live" images with your current algorithm.

    The tree image has a single dominant angle (the horizon), so in this case, you could use the mode of the histogram (the most frequent angle). But that will not work for every image:

    Here you have two peaks. Cyclic correlation should still orient two images to each other, but simply using the mode is probably not enough.

    Also note that the peak in the angle histogram is not "up": In the tree image above, the peak in the angle histogram is probably the horizon. So it's pointing up. In the Lena image, it's the vertical white bar in the background - so it's pointing to the right. Simply orienting the images using the most frequent angle will not turn every image with the right side pointing up.

    This image has even more peaks: Using the mode (or, probably, any single angle) would be unreliable to orient this image. But the angle histogram as a whole should still give you a reliable orientation.

    Note: I didn't pre-process the images, I didn't try gradient operators at different scales, I didn't post-process the resulting histogram. In a real-world application, you would tweak all these things to get the best possible algorithm for a large set of test images. This is just a quick test to see if the idea could work at all.

    Add: To orient two images using this histogram, you would

    1. Normalize all histograms, so the area under the histogram is the same for each image (even if some are brighter, darker or blurrier)
    2. Take the histograms of the images, and compare them for each rotation you're interested in:

    For example, in C#:

    for (int rotationAngle = 0; rotationAngle < 360; rotationAngle++)
    {
       int difference = 0;
       for (int i = 0; i < 360; i++)
          difference += Math.Abs(histogram1[i] - histogram2[(i+rotationAngle) % 360]);
       if (difference < bestDifferenceSoFar)
       {
          bestDifferenceSoFar = difference;
          foundRotation = rotationAngle;
       }
    }
    

    (you could speed this up using FFT if your histogram length is a power of two. But the code would be a lot more complex, and for 256 bins, it might not matter that much)

    这篇关于如何有效地计算平均&QUOT;方向&QUOT;在灰度图像的像素?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

    查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆