Обработка изображений: как выполнить эту функцию с помощью lockbits - PullRequest
0 голосов
/ 26 января 2019

У меня проблема. Мне нужно выполнить эту функцию с помощью lockbits. Пожалуйста, мне нужна помощь.

 public void xPix(Bitmap bmp, int n, Color cx, Color nx)
    {
        try
        {

            for (int y = 0; y < bmp.Height; y++)
            {
                   for (int x = 0; x < bmp.Width; x += (n * 2))
                  {
                       cx = bmp.GetPixel(x, y);
                       if (x + n <= bmp.Width - 1) nx = bmp.GetPixel(x + n, y);
                       bmp.SetPixel(x, y, nx);
                       if (x + n <= bmp.Width - 1) bmp.SetPixel(x + n, y, cx);
                  }
            }
        }
        catch { }
    }

Ответы [ 2 ]

0 голосов
/ 28 января 2019

Если вам нужно более быстрое действие на изображении, вы можете использовать Marshall.Copy метод с Parallel.For

Почему не используется метод GetPixel ? Потому что каждый раз, когда вы звоните, все ваше изображение загружается в память. GetPixel получит один пиксель и разгрузит все изображение. И на каждой итерации ВСЕ изображение загружается в память (например, если вы работаете с изображением размером 500x500 пикселей, GetPixel будет загружать в память 500x500 раз целых пикселей). Когда вы работаете с изображениями в C # (CV), работайте с необработанными bytes из памяти.

Я покажу, как использовать с Lockbits в Бинаризация , потому что это легко объяснить.

 int pixelBPP = Image.GetPixelFormatSize(resultBmp.PixelFormat) / 8;

        unsafe
        {
            BitmapData bmpData = resultBmp.LockBits(new Rectangle(0, 0, resultBmp.Width, resultBmp.Height), ImageLockMode.ReadWrite, resultBmp.PixelFormat);

            byte* ptr = (byte*)bmpData.Scan0; //addres of first line

            int height = resultBmp.Height;
            int width = resultBmp.Width * pixelBPP;

            Parallel.For(0, height, y =>
            {
                byte* offset = ptr + (y * bmpData.Stride); //set row
                for(int x = 0; x < width; x = x + pixelBPP)
                {
                    byte value = (offset[x] + offset[x + 1] + offset[x + 2]) / 3 > threshold ? Byte.MaxValue : Byte.MinValue;
                    offset[x] = value;
                    offset[x + 1] = value;
                    offset[x + 2] = value;

                    if (pixelBPP == 4)
                    {
                        offset[x + 3] = 255;
                    }
                }
            });

            resultBmp.UnlockBits(bmpData);
        }

Теперь, пример с Marshall.copy:

BitmapData bmpData = resultBmp.LockBits(new Rectangle(0, 0, resultBmp.Width, resultBmp.Height), 
                                                ImageLockMode.ReadWrite, 
                                                resultBmp.PixelFormat
                                                );


        int bytes = bmpData.Stride * resultBmp.Height;
        byte[] pixels = new byte[bytes];

        Marshal.Copy(bmpData.Scan0, pixels, 0, bytes); //loading bytes to memory
        int height = resultBmp.Height;
        int width  = resultBmp.Width;

        Parallel.For(0, height - 1, y => //seting 2s and 3s
        {
            int offset = y * stride; //row
            for (int x = 0; x < width - 1; x++)
            {
                int positionOfPixel = x + offset + pixelFormat; //remember about pixel format!
                    //do what you want with pixel
                }
            }
        });

       Marshal.Copy(pixels, 0, bmpData.Scan0, bytes); //copying bytes to bitmap
        resultBmp.UnlockBits(bmpData);

Помните, что при работе с RAW bytes очень важно помнить о PixelFormat. Если вы работаете с RGBA изображением, вам нужно настроить каждый канал. (например offset + x + pixelFormat). Я показал это в Binarization примере, как обработать изображение RGBA с необработанными данными. Если lockbits не достаточно быстро, используйте Marshall.Copy

0 голосов
/ 26 января 2019

Было много вещей, которые не имели смысла для меня в вашем коде. Я исправил фрагменты, которые мешали появлению изображения, и вот результат. Я объясню свои изменения после кода.

  public void xPix(Bitmap bmp, int n, Color cx, Color nx)
  {
     var img = bmp.LockBits(new Rectangle(Point.Empty, bmp.Size), System.Drawing.Imaging.ImageLockMode.ReadWrite, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
     byte[] bmpBytes = new byte[Math.Abs(img.Stride) * img.Height];
     System.Runtime.InteropServices.Marshal.Copy(img.Scan0, bmpBytes, 0, bmpBytes.Length);

     for (int y = 0; y < img.Height; y++)
     {
        for (int x = 0; x < img.Width; x+=n*2)
        {
           cx = Color.FromArgb(BitConverter.ToInt32(bmpBytes, y * Math.Abs(img.Stride) + x * 4));
           if (x + n <= img.Width - 1) nx = Color.FromArgb(BitConverter.ToInt32(bmpBytes, y * Math.Abs(img.Stride) + x * 4));
           BitConverter.GetBytes(nx.ToArgb()).CopyTo(bmpBytes, y * Math.Abs(img.Stride) + x * 4);
           if (x + n <= img.Width - 1) BitConverter.GetBytes(cx.ToArgb()).CopyTo(bmpBytes, y * Math.Abs(img.Stride) + (x + n) * 4);
        }
     }
     System.Runtime.InteropServices.Marshal.Copy(bmpBytes, 0, img.Scan0, bmpBytes.Length);
     bmp.UnlockBits(img);
  }

  protected override void OnClick(EventArgs e)
  {
     base.OnClick(e);
     Bitmap bmp = new Bitmap(@"C:\Users\bluem\Downloads\Default.png");
     for (int i = 0; i < bmp.Width; i++)
     {
        xPix(bmp, new Random().Next(20) + 1, System.Drawing.Color.White, System.Drawing.Color.Green);
     }

     Canvas.Image = bmp;
  }
  1. Нет такого класса, как LockBitmap, поэтому я заменил его на результат вызова Bitmap.LockBits напрямую.
  2. Результат LockBits не включает функции для GetPixel и SetPixel, поэтому я сделал то, что обычно делает с результатом LockBits (см. https://docs.microsoft.com/en-us/dotnet/api/system.drawing.bitmap.lockbits?view=netframework-4.7.2), и скопировал данные в вместо байтового массива.
  3. При непосредственном доступе к байтовым данным необходимо выполнить некоторые математические операции, чтобы преобразовать координаты x и y в одномерную координату в массиве байтов, что я и сделал.
  4. При доступе к байтовым данным непосредственно в пиксельном формате System.Drawing.Imaging.PixelFormat.Format32bppArgb необходимо получить доступ к нескольким байтам для преобразования байтовых данных в цвет пикселя, что я сделал с BitConverter.GetBytes, BitConverter.ToInt32, Color.FromArgb и * 1023. *.
  5. Я не думаю, что будет хорошей идеей менять Image в середине рисования. Вы должны либо нарисовать изображение непосредственно во время события Paint, либо изменить изображение вне события Paint и позволить системе нарисовать его. Поэтому я использовал OnClick моей формы для запуска функции.
  6. Первое случайное число, которое я получил, было 0, поэтому мне пришлось добавить 1, чтобы избежать бесконечного цикла.
  7. Параметры cx и nx никогда не используются в качестве входных данных, поэтому я добавил для них произвольные значения цвета. Ваши переменные x и y не были нигде определены / объявлены.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...