所以我想我可以像这样将图像转换为灰度:
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;
}
现在,我想计算像素的平均“方向”。
我的意思是我想看一个 3x3 的区域,然后如果左侧比右侧更暗,那么方向就是向右,如果底部比顶部更暗,那么方向是向上的,如果左下角比右上角暗,那么方向就是右上。 (想想每个 3x3 区域上的小矢量箭头)。也许一个更好的例子是,如果您在 photoshop 中绘制灰度渐变,并且想要计算他们以什么角度绘制它。
我做过像这个 MatLab 这样的东西,但那是几年前的事了。我想我可以使用类似于
ColorMatrix
的矩阵计算这个,但我不太确定如何。它看起来像 this function可能是我想要的;我可以将它转换为灰度(如上所述),然后用灰度矩阵做一些事情来计算这些方向吗?IIRC,我想要的和edge detection很相似.
在计算这些方向向量后,我将遍历它们并计算图像的平均方向。
最终目标是我想旋转图像,使它们的平均方向始终向上;这样,如果我有两个相同的图像,除了一个旋转(90,180 度或 270 度),它们最终会以相同的方式定向(我不担心一个人最终是否颠倒)。
*剪辑* 删除一些垃圾邮件。你可以查看你想阅读我的其余尝试的修订。
最佳答案
计算角度的平均值通常是一个坏主意:
...
sum += Math.Atan2(yi, xi);
}
}
double avg = sum / (img.Width * img.Height);
一组角的均值没有明确的含义:例如,一个角朝上,一个角朝下,就是一个角朝右。那是你要的吗?假设“向上”是+PI,那么几乎向上的两个角度之间的平均值就是向下的角度,如果一个角度是PI-[某个小值],另一个是-PI+[某个小值]。这可能不是你想要的。此外,您完全忽略了边缘的强度 - 现实生活中的大多数像素根本不是边缘,因此梯度方向主要是噪声。
如果要计算“平均方向”之类的东西,则需要将向量而不是角度相加,然后在循环后计算 Atan2。问题是:这个向量和没有告诉你图像内的对象,因为指向相反方向的梯度会相互抵消。它只告诉您有关图像第一行/最后一行和第一/最后一列之间亮度差异的信息。这可能不是你想要的。
我认为定位图像的最简单方法是创建一个角度直方图:创建一个具有(例如)360 个 bin 的数组,用于 360° 的梯度方向。然后计算每个像素的梯度角度和幅度。将每个梯度幅度添加到直角箱。这不会给你一个单一的角度,而是一个角度直方图,然后可以使用简单的循环相关来将两个图像相互定向。
这是一个概念验证的 Mathematica 实现,我把它放在一起,看看这是否可行:
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]"]
)
示例图像的结果:
角度直方图还说明了为什么平均角度不起作用:直方图本质上是一个尖峰,其他角度大致均匀。此直方图的平均值将始终由统一的“背景噪声”支配。这就是为什么您使用当前算法为每个“真实实时”图像获得几乎相同的角度(大约 180°)的原因。
树图像有一个主角度(地平线),因此在这种情况下,您可以使用直方图的模式(最常见的角度)。但这不适用于每个图像:
这里有两个峰。循环相关仍应使两个图像相互定向,但仅使用该模式可能还不够。
另请注意,角度直方图中的峰值不是“向上”:在上面的树图像中,角度直方图中的峰值可能是地平线。所以它是向上的。在 Lena 图像中,它是背景中的垂直白色条 - 所以它指向右侧。简单地使用最常见的角度定向图像不会使每个图像的右侧都朝上。
这个图像有更多的峰值:使用模式(或者,可能,任何单个角度)都不可靠地定位这个图像。但是角度直方图作为一个整体仍然应该给你一个可靠的方向。
注:我没有对图像进行预处理,也没有尝试不同尺度的梯度算子,也没有对生成的直方图进行后处理。在现实世界的应用程序中,您将调整所有这些内容,以获得适用于大量测试图像的最佳算法。这只是一个快速测试,看看这个想法是否可行。
地址:要使用此直方图定向两个图像,您需要
例如,在 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;
}
}
(如果您的直方图长度是 2 的幂,您可以使用 FFT 加快速度。但代码会复杂得多,对于 256 个 bin,它可能无关紧要)
关于c# - 如何有效地计算灰度图像中像素的平均 "direction"?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10167183/