30

enter image description here

Hi i have a bmp loaded to a BMP object and im required to travel though the pixels as the above image from (1,1) pixel to (100,100) px . using getpixel() method. I was using was ONE loop but it was not successful .

If im using the concept of multidimensional array what should be variable values ?

Ry-
  • 218,210
  • 55
  • 464
  • 476
Sudantha
  • 15,684
  • 43
  • 105
  • 161
  • You could always try... **TWO** loops, and see if they are successful... :) What question are you actually asking? How to traverse a 2D-array of pixels, or something about variable values? – Dan J May 16 '11 at 16:36
  • 4
    The diagram looks like all the odd rows are traversed left to right, and the evens right to left. Is that the intent? – Kit May 25 '11 at 15:21

7 Answers7

57

When you want to doing image processing on huge images GetPixel() method takes long time but I think my algorithm takes less time than other answers , for example you can test this code on 800 * 600 pixels image.


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]);
    }
}
jcvandan
  • 14,124
  • 18
  • 66
  • 103
hashi
  • 2,520
  • 3
  • 22
  • 25
  • 2
    I've used a similar solution and it works great. You just have to make sure your source bmp is the same pixel format! You also inverted row and column loops (just the names, as the implementation still works) – Jay May 26 '11 at 21:45
  • isn't faster to run through columns ? first for (int row = 0; row < bmpData.Width; row++) and then for(int column = 0; column < bmpData.Height; column++). – Ami DATA Jan 15 '14 at 09:18
  • @AmiDATA No, bitmaps (and most 2D formats) are in row-major format, which means the pixels are laid out in memory row by row. – Thomas Jan 19 '14 at 13:32
  • Thomas, because it's row-major the inner loop should be on the columns and not rows. just give it a test. [wiki](http://en.wikipedia.org/wiki/Row-major_order). – Ami DATA Feb 04 '14 at 16:11
  • @AmiDATA The inner loop is the one that is run sequentially. If it's the columns then it means data will be accessed in column-major order (e.g. [0, 0], [0, 1], ... [0, height - 1], [1, 0], ...). Are you getting the two confused? – Thomas Feb 04 '14 at 17:31
  • Thomas, In row-major storage, memory is organized such that rows are stored one after the other. so there for you should go through one row after the other. one row means to go column after column before passing to the next row. did you look at the link or rather run code ? I did both.. Yes, I'm confused. – Ami DATA Feb 04 '14 at 17:43
  • @AmiDATA Print out the order of iterations e.g. `print(row, col)` inside the loop for both cases. In row-major storage you want to run across each column of a row before going to the next row. Which order achieves this? Why? – Thomas Feb 05 '14 at 13:38
  • @Thomas I think you're confused, Ami is correct. Did you try the exercise you mentioned? The solution is clearly `row` as the outer loop and `col` as the inner. – Alex Feb 17 '14 at 10:25
  • @AlexG Ah, I see, we have a terminology problem. My bad. So to go in row-major order, we run over columns in the outer loop, and run over rows in the inner loop, which means that the y-coordinate (vertical) is iterated in the outer loop and the x-coordinate (horizontal) is iterated in the inner loop. Indeed perhaps "height" and "width" would have been more expressive than "rows" and "cols". I apologize for the confusion however I would still like to point out that the post in question is correct and that Ami misunderstood the snippet while I misunderstood his question... – Thomas Feb 17 '14 at 11:00
  • @Thomas But clearly when you enumerate the `cols` then you should be using the `x` variable and `rows` should be `y`. I have forked the code here: http://ideone.com/IhT5dS. As you can see, row-major has the `x` variable (the column) in the inner loop – Alex Feb 17 '14 at 11:24
  • @AlexG Yes, the *columns* (x) variable is in the inner loop, but the inner loop iterates across pixels that are on the same *row* - that's where the mixup is occurring. You are correct that my snippet is in error and I apologize for sounding dismissive. – Thomas Feb 17 '14 at 11:32
  • 5
    you may want to add `bmp.UnlockBits(bmpData); ` after done copying. – C19 Jun 15 '16 at 08:04
  • Great Code - very fast and efficient. I was trying to do parallel.For loops and this was even faster. Just note, if you want to save the X,Y coordinates for each pixel for any reason, rows(inner loop) are the X and Columns(outer loop) are Y. Also, do not forget to unlock and dispose or your image when finished. Thanks again for the great solutions! – Toby Jun 14 '19 at 16:14
9

if you want to traverse it right, left, right, ... in one loop, this would do it:

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);
}
Lorenz Lo Sauer
  • 23,698
  • 16
  • 85
  • 87
king_nak
  • 11,313
  • 33
  • 58
7

You need to use two loops:

for (int ii = 0; ii < 100; ii++)
{
  for (int jj = 0; jj < 100; jj++)
  {
    Color pixelColor = bitmap.GetPixel(ii, jj);
    // do stuff with pixelColor
  }
}
thomson_matt
  • 7,473
  • 3
  • 39
  • 47
  • You don't "need" to but it certainly aids readability and good programming practice. Double letter loop variables are already a good start :) – Lorenz Lo Sauer Jul 12 '16 at 03:53
6

You cold use a Linq selection to obtain a IEnumerable object:

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);

...then iterate on the IEnumerable (using implicit typing):

foreach(var color in pixelColors)
{
    //do stuff on RGB values, etc...
}
Andrea Pigazzini
  • 359
  • 1
  • 14
6

You can turn it into an easy-to-access multidimensional array of colors like so:

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
}

You would call it like so:

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
    }
}

And you could traverse the returned array quite quickly in any direction you want at all.

Ry-
  • 218,210
  • 55
  • 464
  • 476
  • multidimensional array doesnt work on this matter check and see cz multidimensional array doesnt go by `line by line` – Sudantha May 27 '11 at 04:10
  • @Sudantha: Can you be a bit more clear on that? A multidimensional array does work to represent a block of pixels, you know. – Ry- May 27 '11 at 18:00
4

Though the two nested loop approach is typically "better" or more readable, you can do it in 1 loop like this:

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);
}

Or slightly better, change the first line to:

var numberOfPixels = bmp.Height * bmp.Width;
for(int i = 0; i < numberOfPixels; i++)
CodingWithSpike
  • 42,906
  • 18
  • 101
  • 138
4

You can try something like this

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);
   }
}

or you can split internal cycle to: left to right and right to left

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);
         }
       }
    }
Viacheslav Smityukh
  • 5,652
  • 4
  • 24
  • 42