Sobel运算符和使用WriteableBitmapEx的卷积

所以我在Windows RT上使用WriteableBitmapEx作为应用程序。 我正在尝试使用sobel算子在图像上实现边缘检测。 我已经使用.Convolute()成功地将用于x和y检测的两个内核应用到图像上,但现在我卡住了将两个图像添加到一个。 问题是,两个图像的所有像素似乎都具有透明度值0(因此ARGB中的A)。 我可以自己显示两个图像而没有问题,但添加它们只给我一张黑色图片。 所以我的问题是:

  • 为什么卷积后每个像素的透明度设置为0?
  • 为什么我仍然可以显示图像而不是全黑?
  • 添加两张图片时为什么会黑?
  • 是否有更好的方法来组合两个图像? Blit unfortunatley似乎不支持这种像素添加。 但ForEach真的很慢……

为了进行校验,到目前为止这是我的代码。 我可以显示wbmpY和wbmpX,但是finalbmp是完全黑色的。

public int[,] sobelY = new int[3, 3] { { 1, 2, 1 }, { 0, 0, 0 }, { -1, -2, -1 } }; public int[,] sobelX = new int[3, 3] { { -1, 0, 1 }, { -2, 0, 2 }, { -1, 0, 1 } }; public void trim(WriteableBitmap wbmp) { var graybmp = wbmp.Clone(); graybmp.ForEach(toGrayscale); var wbmpY = graybmp.Clone(); var wbmpX = graybmp.Clone(); wbmpY = wbmpY.Convolute(sobelY, 1, 0); wbmpX = wbmpX.Convolute(sobelX, 1, 0); var finalbmp = combineSobel(wbmpX, wbmpY); } public WriteableBitmap combineSobel(WriteableBitmap img, WriteableBitmap img2) { int height = img.PixelHeight; int width = img.PixelWidth; WriteableBitmap result = img.Clone(); for (int x = 0; x < width; x++) { for (int y = 0; y < height; y++) { Color imgColor = img.GetPixel(x, y); Color img2Color = img2.GetPixel(x, y); Color newColor = Color.FromArgb( Math.Min((byte)Math.Sqrt(Math.Pow(imgColor.A, 2) + Math.Pow(img2Color.A, 2)), (byte)255), Math.Min((byte)Math.Sqrt(Math.Pow(imgColor.R, 2) + Math.Pow(img2Color.R, 2)), (byte)255), Math.Min((byte)Math.Sqrt(Math.Pow(imgColor.G, 2) + Math.Pow(img2Color.G, 2)), (byte)255), Math.Min((byte)Math.Sqrt(Math.Pow(imgColor.B, 2) + Math.Pow(img2Color.B, 2)), (byte)255) ); result.SetPixel(x, y, newColor); } } return result; } 

卷积应用于所有可用频道。 不仅处理红色,绿色和蓝色(在这种情况下是你想要的),而且还处理alpha通道。 这导致alpha值为零(100%透明)。 请考虑以下示例:

 1 0 -1 255 255 255
 2 0 -2超过255 255 255
 1 0 -1 255 255 255
 1 * 255 0 * 255 -1 * 255 255 0 -255
 2 * 255 0 * 255 -2 * 255 = 510 0 -510
 1 * 255 0 * 255 -1 * 255 255 0 -255

对于所有像素,2 * 255 + 510 + 3 * 0  -  2 * 255  -  510 = 0

从技术上讲,它一切都很好,它没有检测到alpha通道上的任何边缘。 但从function上来说,这不是你想要的。 如果不需要此行为,您可以跳过处理Alpha通道(如果源允许)或之后将alpha重置为255。

我将推测将在屏幕上显示的黑色图像,因为我没有使用过的技术的经验。 许多框架首先将图像重置为纯色(假设在这种情况下它是黑色的)。 这是必需的,以便在处理透明图像(或部分透明图像)时,前一个图像不会渗透。 将复杂(透明)图像添加到此纯色将产生相同的纯色。 因此,图像将全部显示为黑色。

注意:combineSobel使用所有通道,但由于它之前已转换为灰度,您可能希望优化颜色的创建。

是否有更好的方法来组合两个图像? Blit unfortunatley似乎不支持这种>像素添加。 但ForEach真的很慢……

出于这个原因你应该使用不安全的代码,检查一下: 为什么我的不安全代码阻塞比我的安全代码慢?

作为速度问题,请考虑为每个像素调用16个函数(4xMath.Min,4xMath.Pow,4xMath.Sqrt)。 这是一个巨大的开销。

像素值的范围为[0,255] Math.Pow([0,255],2)+ Mat.Pow([0,255],2)得到的范围为[0,2 * Math.Pow(255,2)] => [0,130 050]我会像这样构建一个查找表:

 byte[] LUT4Sqrt = new byte[130051]; for (int i = 0; i < 130051; i++) { LUT4Sqrt[i] = (byte)(Math.Sqrt(i)); } 

Math.Pow的查找表也可以制作。

您的代码中有2个问题:

1)我试过并且旋转方法对我不起作用 – 它只是创建一个空图像。 我想这不是你的错。 当您合并2个空图像时,您将获得空图像。

2)您的联合收割机实施存在一个小问题。 您可以将透明度设置为255而不使用sqrt计算,就像在r,g,b上一样

希望这可以帮助