Перемещение по пикселям в BMP

#c# #.net #image-processing

#c# #.net #обработка изображений

Вопрос:

введите описание изображения здесь

Привет, у меня есть bmp загруженный в BMP объект, и мне требуется перемещаться по пикселям, как на изображении выше, от (1,1) пикселя к (100,100) px. используя getpixel() метод. Я использовал был ОДИН цикл, но он не был успешным.

Если я использую концепцию многомерного массива, какими должны быть значения переменных?

Комментарии:

1. Вы всегда можете попробовать… ДВА цикла и посмотрите, успешны ли они … 🙂 Какой вопрос вы на самом деле задаете? Как перемещаться по 2D-массиву пикселей или что-то о значениях переменных?

2. Диаграмма выглядит так, как будто все нечетные строки перемещаются слева направо, а четные — справа налево. Это намерение?

Ответ №1:

Если вы хотите выполнить обработку изображений на огромных изображениях, метод GetPixel () занимает много времени, но я думаю, что мой алгоритм занимает меньше времени, чем другие ответы, например, вы можете протестировать этот код на изображении размером 800 * 600 пикселей.


 Bitmap bmp = new Bitmap("SomeImage");

// Lock the bitmap's bits.  
Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
BitmapData bmpData = bmp.LockBits(rect, ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);

// Get the address of the first line.
IntPtr ptr = bmpData.Scan0;

// Declare an array to hold the bytes of the bitmap.
int bytes = bmpData.Stride * bmp.Height;
byte[] rgbValues = new byte[bytes];
byte[] r = new byte[bytes / 3];
byte[] g = new byte[bytes / 3];
byte[] b = new byte[bytes / 3];

// Copy the RGB values into the array.
Marshal.Copy(ptr, rgbValues, 0, bytes);

int count = 0;
int stride = bmpData.Stride;

for (int column = 0; column < bmpData.Height; column  )
{
    for (int row = 0; row < bmpData.Width; row  )
    {
        b[count] = (byte)(rgbValues[(column * stride)   (row * 3)]);
        g[count] = (byte)(rgbValues[(column * stride)   (row * 3)   1]);
        r[count  ] = (byte)(rgbValues[(column * stride)   (row * 3)   2]);
    }
}
  

Комментарии:

1. Я использовал похожее решение, и оно отлично работает. Вам просто нужно убедиться, что ваш исходный bmp имеет тот же формат пикселей! Вы также инвертировали циклы строк и столбцов (только имена, поскольку реализация все еще работает)

2. не быстрее ли проходить по столбцам? сначала для (int row = 0; строка < bmpData. Ширина; строка ), а затем для(int column = 0; столбец < bmpData. Высота; столбец ).

3. @AmiDATA Нет, растровые изображения (и большинство 2D форматов) представлены в формате row-major, что означает, что пиксели располагаются в памяти строка за строкой.

4. Томас, поскольку он строковый, внутренний цикл должен быть по столбцам, а не по строкам. просто проведите тест. Вики .

5. возможно, вы захотите добавить bmp.UnlockBits(bmpData); после завершения копирования.

Ответ №2:

если вы хотите перемещаться вправо, влево, … за один цикл, это будет сделано:

 for (int i = 0 ; i < bmp.Height * bmp.Width;   i) {
    int row = i / bmp.Height;
    int col = i % bmp.Width;
    if (row%2 != 0) col = bmp.Width - col-1;
    var pixel = bmp.GetPixel(col, row);
}
  

Комментарии:

1. если (строка%2 != 0) c = bmp. Ширина-col-1; должно быть, если (строка%2 != 0) col = bmp. Ширина-col-1;

2. GetPixel является очень дорогим и медленным. Гораздо лучше использовать BitmapData в соответствии с ответом.

Ответ №3:

Вам нужно использовать два цикла:

 for (int ii = 0; ii < 100; ii  )
{
  for (int jj = 0; jj < 100; jj  )
  {
    Color pixelColor = bitmap.GetPixel(ii, jj);
    // do stuff with pixelColor
  }
}
  

Комментарии:

1. Вам это «не нужно», но это, безусловно, способствует удобочитаемости и хорошей практике программирования. Переменные цикла с двумя буквами уже являются хорошим началом 🙂

Ответ №4:

Для получения объекта с IEnumerable можно холодно использовать выделение Linq:

 var pixelColors =
    from x in Enumerable.Range(0, bmp.Width - 1)
    from y in Enumerable.Range(0, bmp.Height - 1)
    select bmp.GetPixel(x, y);
  

… затем выполните итерацию по IEnumerable (используя неявную типизацию):

 foreach(var color in pixelColors)
{
    //do stuff on RGB values, etc...
}
  

Комментарии:

1. не могли бы вы сказать мне, пожалуйста, работает ли linq быстрее, чем два цикла? пожалуйста, сообщите мне

Ответ №5:

Вы можете превратить это в простой в доступе многомерный массив цветов, например:

 using System.Drawing.Imaging;
using System.Runtime.InteropServices;

// ...

Color[,] GetSection(Image img, Rectangle r) {
    Color[,] r = new Color[r.Width, r.Height]; // Create an array of colors to return

    using (Bitmap b = new Bitmap(img)) { // Turn the Image into a Bitmap
        BitmapData bd = b.LockBits(r, ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb); // Lock the bitmap data
        int[] arr = new int[b.Width * b.Height - 1]; // Create an array to hold the bitmap's data
        Marshal.Copy(bd.Scan0, arr, 0, arr.Length); // Copy over the data
        b.UnlockBits(bd); // Unlock the bitmap

        for (int i = 0; i < arr.Length; i  ) {
            r[i % r.Width, i / r.Width] = Color.FromArgb(arr[i]); // Copy over into a Color structure
        }
    }

    return r; // Return the result
}
  

Вы бы назвали это примерно так:

 Color[,] c = GetSection(myImage, new Rectangle(0, 0, 100, 100)); // Get the upper-left 100x100 pixel block in the image myImage
for (int x = 0; x < c.GetUpperBound(0); x  ) {
    for (int y = 0; y < c.GetUpperBound(1); y  ) {
        Color thePixel = c[x, y];
        // do something with the color
    }
}
  

И вы могли бы довольно быстро перемещаться по возвращаемому массиву в любом направлении, которое вы хотите вообще.

Комментарии:

1. многомерный массив не работает по этому вопросу проверьте и убедитесь, что многомерный массив cz не проходит line by line

2. @Sudantha: Не могли бы вы пояснить это немного яснее? Вы знаете, что многомерный массив работает для представления блока пикселей.

Ответ №6:

Хотя подход с двумя вложенными циклами обычно «лучше» или более удобочитаем, вы можете сделать это за 1 цикл следующим образом:

 for(int i = 0; i < bmp.Height * bmp.Width; i  )
{
    int row = i / bmp.Width;
    int col = i % bmp.Width;
    var pixel = bmp.GetPixel(col, row);
}
  

Или, что немного лучше, измените первую строку на:

 var numberOfPixels = bmp.Height * bmp.Width;
for(int i = 0; i < numberOfPixels; i  )
  

Ответ №7:

Вы можете попробовать что-то вроде этого

 for(int y = 0; y < bmp.Height; y  )
{
   var even = y % 2 == 0;
   var startX = even ? 0 : bmp.Width - 1;
   var endX = even ? bmp.Width : -1;
   var delta = even ? 1 : -1; 

   for(int x = startX; x != endX; x  = delta)
   {
      var pixel = bmp.GetPixel(x,y);
   }
}
  

или вы можете разделить внутренний цикл на: слева направо и справа налево

 for(int y = 0; y < bmp.Height; y  = 2)
    {
       for(int x = 0; x < bmp.Width; x  )
       {
          var pixel = bmp.GetPixel(x,y);
       }

       var line = y   1;

       if(line < bmp.Height)
       {
         for(int x = bmp.Width; x >= 0; --x)
         {
           var pixel = bmp.GetPixel(x,line);
         }
       }
    }