Выполнение операций с массивами на растровом изображении - PullRequest
0 голосов
/ 03 мая 2019

Я пытаюсь перенести некоторый код из старого проекта Python в текущий проект C # .

Код Python, который я пытаюсь адаптировать, применяет фильтр sobel к каждому из цветовых каналов RGB в изображении, а затем использует некоторую базовую линейную алгебру для сшивания результатов канала. вместе в градациях серого изображение изолированных краев.

Проблема, с которой я сталкиваюсь, заключается в том, что в то время как python позволяет легко обрабатывать изображения как простые числовые массивы, и, таким образом, можно произвольно брать их фрагменты и выполнять над ними операции линейной алгебры ( C # более привередлив в отношении набора текста).

Мне нужно сделать матричное умножение результатов, которые я получаю при запуске фильтра sobel на отдельных каналах изображения, но неясно, как втиснуть растровое изображение в форму, которая легко это допустит.

Я видел кое-что о преобразовании в byte массив , но не уверен, что это подойдет для моих целей. Я бы просто хотел обычный int или float массив , содержащий значения пикселей.

Я мог бы проходить по пикселям и выполнять побитовые операции с ними, но это должно замедлить процесс, поэтому мне было интересно, есть ли хороший способ выполнить matrix операции над растровым изображением или преобразование растрового изображения в форму, которая будет подпадает под это.

Я не уверен точно, насколько это было бы полезно, но если это поможет проиллюстрировать то, о чем я говорю, вот мой код на 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 голосов
/ 03 мая 2019

Вам нужно будет перейти на более низкий уровень в C # и написать корреляции самостоятельно.

Прежде всего вам нужно будет получить цвета для каждого пикселя в растровом изображении и создайте матрицу со всей этой информацией.

Bitmap.GetPixel (Int32, Int32)

Получает цвет указанного пикселяв этом растровом изображении.

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

Затем сопоставьте и умножьте матрицы.Для этого вы можете обратиться к библиотеке третьей части (например, NumPy в мире Python) или сделать это вручную.

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 };
    }
}
0 голосов
/ 03 мая 2019

Итак, ответ Тима выше о блокировке битов чрезвычайно полезен в качестве общего решения для такого рода проблем. Однако по причинам, о которых я не буду здесь говорить, так как проблемы - это очень специфические проблемы совместимости с некоторыми из моих внешних зависимостей, это не сработало для меня. Я закончил тем, что делал то, что я сказал, что я не хотел делать, и перебирал изображение, чтобы сделать мою математику. Это все еще работало в конце и не замедляло программу на количество, заметное пользователю. Однако в общем случае это может быть неверно, поскольку оптимизация по скорости не является главным приоритетом данного конкретного приложения, а предназначена для многих других, которые выполняют аналогичные операции. В итоге я создал массивы с двойными числами того же размера, что и массивы изображений, и затем начал выполнять вычисления по этим массивам. После того, как вся математика была сделана, я использовал 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

Собел на оттенках серого (по умолчанию используется библиотека Aforge)

VS.

Sobel on Color Image

Sobel on Color (используя код выше)

0 голосов
/ 03 мая 2019

Предполагая, что вы работаете с System.Drawing.Bitmap в этом примере, когда вы ссылаетесь на Bitmap, вы захотите использовать метод Bitmap.LockBits(), чтобы получить указатель на необработанные биты в битовой карте.Затем вы можете использовать небезопасный код (быстрее, но вы лучше знаете, что делаете) или скопировать растровые данные в управляемую память и из нее в виде байтовых массивов, чтобы манипулировать ею.

Вот официальная документация по LockBits()и у них есть разумный пример (с использованием безопасной, но более медленной техники копирования и извлечения из управляемой памяти) https://docs.microsoft.com/en-us/dotnet/api/system.drawing.bitmap.lockbits?view=netframework-4.8

...