在位图上执行数组操作

时间:2019-05-02 22:52:27

标签: c# winforms image-processing aforge

我正在尝试将一些代码从旧的python项目迁移到当前的 C#项目。

我尝试修改的python代码将 sobel 滤镜应用于图像中的每个 RGB 颜色通道,然后使用一些基本的线性代数来缝合通道结果一起成为孤立边缘的灰度图像。

我遇到的问题是,尽管python可以轻松地将图像视为简单的数字数组,所以允许人们对其进行切片并对其进行线性代数运算( C#< / em>在键入方面更为挑剔。

我需要对在单个图像通道上运行 sobel 过滤器得到的结果进行矩阵乘法,但是尚不清楚如何将位图转换为易于允许的形式。

我已经看到了转换为byte array 的东西,但是不确定这是否对我有用。我真的很喜欢包含像素值的常规int或float array

我可以遍历像素并对其执行按位操作,但这势必会减慢速度,因此我想知道是否有一种好的方法可以执行矩阵对位图进行操作,或将位图转换为适合此格式的形式。

我不确定它到底有多有用,但是如果它有助于说明我在说什么,这是我的python代码:

image_array = numpy.float64(scaled_image)

R_x = scipy.ndimage.filters.correlate(image_array[:, :, 0], [[1, 2, 1], [0, 0, 0], [-1, -2, -1]])
G_x = scipy.ndimage.filters.correlate(image_array[:, :, 1], [[1, 2, 1], [0, 0, 0], [-1, -2, -1]])
B_x = scipy.ndimage.filters.correlate(image_array[:, :, 2], [[1, 2, 1], [0, 0, 0], [-1, -2, -1]])

R_y = scipy.ndimage.filters.correlate(image_array[:, :, 0], [[1, 0 , -1], [2, 0, -2], [1, 0, -1]])
G_y = scipy.ndimage.filters.correlate(image_array[:, :, 1], [[1, 0 , -1], [2, 0, -2], [1, 0, -1]])
B_y = scipy.ndimage.filters.correlate(image_array[:, :, 2], [[1, 0 , -1], [2, 0, -2], [1, 0, -1]])

Jacobian_x = R_x**2 + G_x**2 + B_x**2
Jacobian_y = R_y**2 + G_y**2 + B_y**2
Jacobian_xy = R_x * R_y + G_x * G_y + B_x * B_y
D = numpy.sqrt(numpy.fabs((Jacobian_x**2) - (2 * Jacobian_x * Jacobian_y) + (Jacobian_y**2) + 4 * (Jacobian_xy**2)))
E = (Jacobian_x + Jacobian_y + D) / 2
Edges = numpy.sqrt(E)

到目前为止,我对等的C#代码在哪里:

Bitmap newImage = resize.Apply(bmp);
Bitmap RedImage = extractRed.Apply(newImage);
Bitmap GreenImage = extractGreen.Apply(newImage);
Bitmap BlueImage = extractBlue.Apply(newImage);

Bitmap Rx = SobelX.Apply(RedImage);
Bitmap Gx = SobelX.Apply(GreenImage);
Bitmap Bx = SobelX.Apply(BlueImage);

Bitmap Ry = SobelY.Apply(RedImage);
Bitmap Gy = SobelY.Apply(GreenImage);
Bitmap By = SobelY.Apply(BlueImage);

***Where all my math would go.
   Jacobian_x = yadda yadda yadda***

在此先感谢任何可以提供帮助的人!

3 个答案:

答案 0 :(得分:0)

在该示例中,假设您正在使用System.Drawing.Bitmap,当您引用Bitmap时,您将要使用Bitmap.LockBits()方法来获取指向位图中原始位的指针。然后,您可以使用不安全的代码(速度更快,但您最好知道自己在做什么),也可以将位图数据作为字节数组与托管内存进行复制,以进行操作。

这是LockBits()上的官方文档,并且有一个合理的示例(使用安全但较慢的“从源复制到源存储”技术) https://docs.microsoft.com/en-us/dotnet/api/system.drawing.bitmap.lockbits?view=netframework-4.8

答案 1 :(得分:0)

您需要进入C#的较低级别并自己编写相关性。

首先,您必须检索colors for each pixel in the Bitmap,并使用所有信息构建矩阵。

  

Bitmap.GetPixel(Int32,Int32)

     

获取此位图中指定像素的颜色。

     

public System.Drawing.Color GetPixel(int x,int y);

然后关联并乘以矩阵。为了实现这一点,您可以引用third-part library(例如Python世界中的NumPy)或手动完成。

using System;
using System.Drawing;
using System.Windows.Forms;

static class Util {
    public static Color[][] ExtractColorArrayFrom(Bitmap bm)
    {
        int height = bm.Height;
        int width = bm.Width;
        var toret = new Color[height][];

        for(int i = 0; i < height; ++i) {
            toret[ i ] = new Color[ width ];

            for(int j = 0; j < width; ++j) {
                toret[ i ][ j ] = bm.GetPixel( i, j );
            }
        }

        return toret;
    }

    public static int[] ARGBFrom(Color c)
    {
        return new int[]{ c.A, c.R, c.G, c.B };
    }
}

答案 2 :(得分:0)

因此,Tim上面关于锁定位的答案作为解决此类问题的通用解决方案非常有用。但是由于种种原因,我不会进入这里,因为这些问题是与我的某些外部依赖关系非常具体的兼容性问题,所以最终没有为我工作。我最终做了我说不想做的事情,并遍历图像进行数学运算。最终它仍然可以工作,并且不会使程序的运行速度降低到用户可以察觉的程度。但是,通常情况可能并非如此,因为优化速度并不是此特定应用程序的头等大事,而是许多执行类似操作的其他应用程序的首要任务。我最终创建了与图像数组大小相同的double数组,然后在这些数组上运行数学运算。完成所有数学运算后,我使用SetPixel遍历空白图像并将这些像素值应用于新的位图。

Bitmap RedImage = extractRed.Apply(newImage);
Bitmap GreenImage = extractGreen.Apply(newImage);
Bitmap BlueImage = extractBlue.Apply(newImage);

Bitmap Rx = SobelX.Apply(RedImage);
Bitmap Gx = SobelX.Apply(GreenImage);
Bitmap Bx = SobelX.Apply(BlueImage);

Bitmap Ry = SobelY.Apply(RedImage);
Bitmap Gy = SobelY.Apply(GreenImage);
Bitmap By = SobelY.Apply(BlueImage);
double[,] JacobianX = new double[Rx.Width, Rx.Height];
double[,] JacobianY = new double[Rx.Width, Rx.Height];
double[,] JacobianXY = new double[Rx.Width, Rx.Height];
double[,] Determinant = new double[Rx.Width, Rx.Height];
double[,] E = new double[Rx.Width, Rx.Height];
Bitmap Edges = new Bitmap(Rx.Width, Rx.Height);

for (int i = 1; i < Rx.Width-1; i++)
    {
    for (int j = 1; j < Rx.Height-1; j++)
        {
        Color redX = Rx.GetPixel(i, j);
        Color greenX = Gx.GetPixel(i, j);
        Color blueX = Bx.GetPixel(i, j);

        Color redY = Ry.GetPixel(i, j);
        Color greenY = Gy.GetPixel(i, j);
        Color blueY = By.GetPixel(i, j);

        JacobianX[i, j] = Math.Pow(redX.R, 2) + Math.Pow(greenX.G, 2) + Math.Pow(blueX.B, 2);
        JacobianY[i, j] = Math.Pow(redY.R, 2) + Math.Pow(greenY.G, 2) + Math.Pow(blueY.B, 2);
        JacobianXY[i, j] = redX.R * redY.R + greenX.G * greenY.G + blueX.B * blueY.B;
        D[i, j] = Math.Sqrt(Math.Abs(Math.Pow(JacobianX[i, j], 2) - (2 * JacobianX[i, j] * JacobianY[i, j]) + (Math.Pow(JacobianY[i, j], 2)) + 4 * Math.Pow(JacobianXY[i, j], 2)));
        E[i, j] = (JacobianX[i, j] + JacobianY[i, j] + D[i, j]) / 2;

        if (Math.Sqrt(E[i, j]) > 255) { E[i, j] = Math.Pow(255,2); }

        Color newcolor = Color.FromArgb(255, (int)Math.Sqrt(E[i, j]), (int)Math.Sqrt(E[i, j]), (int)Math.Sqrt(E[i, j]));
        Edges.SetPixel(i, j, newcolor);
    }
}

这是一个相当明显的进步:

Sobel On Grayscale Image

Sobel灰度(默认使用Aforge库)

VS。

Sobel on Color Image

Sobel on Color(使用上面的代码)