Link to home
Start Free TrialLog in
Avatar of derekthornton
derekthornton

asked on

Foreach, Or Not Foreach.

I've got this method ..or rather series of methods that cycles through every pixel in an image using unmanaged code. One of which is displayed below ( this particular one, in conjunction with some other methods, checks a page to see if it is blank or has anything on it ).
I'd like to come up with a way to just have a template-like method stored in a .dll file, and then cycle through all the pixels using a Foreach loop on an image. For instance..

foreach(UnmanagedPixel p in iImage)
{
 ...
}

...Is this even possible? I've got no idea where to even begin to attempt something like this. Here's the sample method.

            public static Boolean ValidatePage(Image imgImage)
            {
                  // Store our blank pixel count
                  double dblBlankPixels = 0.0;
                  // Store our pixel count
                  double dblTotalPixels = 0.0;
                  // Work with a Bitmap copy
                  Bitmap bmImage = (Bitmap)imgImage.Clone();

                  // Lock the bits
                  BitmapData bmData = bmImage.LockBits
                        (new Rectangle(Point.Empty,bmImage.Size),
                        ImageLockMode.ReadOnly,PixelFormat.Format24bppRgb);

                  // Retrieve the Bitmap Stride Width
                  Int32 Stride = bmData.Stride;
                  // Create Unmanaged memory for this code
                  IntPtr Scan0 = bmData.Scan0;

                  unsafe
                  {
                        byte * p = (byte *)(void *)Scan0;

                        Int32 nOffset = Stride  - bmImage.Width * 3;
                        Int32 nWidth  = bmImage.Width * 3;

                        for(int y = 0; y < bmImage.Height; ++y)
                        {
                              for(int x=0; x < nWidth; ++x )
                              {
                                    if(p[0] >= (byte)(225))
                                    {
                                          dblBlankPixels++;
                                    }
                                    dblTotalPixels++;
                                    ++p;
                              }
                              p += nOffset;
                        }
                  }
                  // Unlock the bitmap
                  bmImage.UnlockBits(bmData);
                  // Destroy it
                  bmImage.Dispose();

                  return ComparePixelData(dblBlankPixels,dblTotalPixels);
            }
Avatar of AlexFM
AlexFM

Not exactly what you want but possibly may help.
Consider the form with two picture boxes and button. See this code:

        Bitmap m_bmp1;
        Bitmap m_bmp2;
       
        private void button1_Click(object sender, System.EventArgs e)
        {
            m_bmp1 = new Bitmap(@"C:\Sample.bmp");
            pictureBox1.Image = m_bmp1;

            m_bmp2 = new Bitmap(m_bmp1);
            ChangeBitmap(m_bmp2);
            pictureBox2.Image = m_bmp2;
        }
         
         
        void ChangeBitmap(Bitmap bmImage)
        {
            // Lock the bits
            BitmapData bmData = bmImage.LockBits
                (new Rectangle(Point.Empty,bmImage.Size),
                ImageLockMode.ReadWrite,PixelFormat.Format24bppRgb);

            // Retrieve the Bitmap Stride Width
            Int32 Stride = bmData.Stride;
            // Create Unmanaged memory for this code
            IntPtr Scan0 = bmData.Scan0;

            unsafe
            {
                byte * p = (byte *)(void *)Scan0;

                Int32 nOffset = Stride  - bmImage.Width * 3;
                Int32 nWidth  = bmImage.Width * 3;

                for(int y = 0; y < bmImage.Height; ++y)
                {
                    for(int x=0; x < nWidth; ++x )
                    {
                        p[0] = (byte)(255 - p[0]);
                        ++p;
                    }
                    p += nOffset;
                }
            }
            // Unlock the bitmap
            bmImage.UnlockBits(bmData);
        }

Simple - when button is pressed, two images are shown. Second image is inverted. But this solution is not generic - if I want to make other transformation, I need to write another ChangeBitmap function. The idea is to pass pointer to callback function (delegate) which makes single pixel conversion. See the following code:

        Bitmap m_bmp1;
        Bitmap m_bmp2;
        delegate void ChangePixelDelegate(ref byte pixel);

        private void button1_Click(object sender, System.EventArgs e)
        {
            m_bmp1 = new Bitmap(@"C:\Sample.bmp");
            pictureBox1.Image = m_bmp1;

            m_bmp2 = new Bitmap(m_bmp1);
            ChangeBitmap(m_bmp2, new ChangePixelDelegate(this.ChangePixel));
            pictureBox2.Image = m_bmp2;
        }

        void ChangePixel(ref byte pixel)
        {
            pixel = (byte)(255 - pixel);
        }

        void ChangeBitmap(Bitmap bmImage, ChangePixelDelegate callback)
        {
            // Lock the bits
            BitmapData bmData = bmImage.LockBits
                (new Rectangle(Point.Empty,bmImage.Size),
                ImageLockMode.ReadWrite,PixelFormat.Format24bppRgb);

            // Retrieve the Bitmap Stride Width
            Int32 Stride = bmData.Stride;
            // Create Unmanaged memory for this code
            IntPtr Scan0 = bmData.Scan0;

            unsafe
            {
                byte * p = (byte *)(void *)Scan0;

                Int32 nOffset = Stride  - bmImage.Width * 3;
                Int32 nWidth  = bmImage.Width * 3;

                for(int y = 0; y < bmImage.Height; ++y)
                {
                    for(int x=0; x < nWidth; ++x )
                    {
                        callback(ref p[0]);
                        ++p;
                    }
                    p += nOffset;
                }
            }
            // Unlock the bitmap
            bmImage.UnlockBits(bmData);
        }


Now I can write another function (for example, ChangeBrightness):

        void ChangeBrightness(ref byte pixel)
        {
            int n = pixel + 20;
            if ( n > 255 )
                n = 255;
            pixel = (byte)n;
        }

and call the same ChangeBitmap function providing another callback:

            ChangeBitmap(m_bmp2, new ChangePixelDelegate(this.ChangeBrightness));

This is basic idea, implementation details may be improved.
Avatar of derekthornton

ASKER

Well, you see the point is that I'm using this same algorithm for a number of image processes, each doing something different, but it's all in the for Loop that it does something different, so I thought it would be smarter to encapsulate the rest in something else.
Actually, you can think about writing your own collection class, which exposes foreach interface for clients. This class gets bitmap reference in constructor and locks bitmap bits. For each Item call it returns appropriate pixel. This may be nice, but I am not sure that it can perform well.
That's kind of what I had in mind but I've no idea where to begin on that.
I tried writing something to Implement IEnumerable and the like, but how to use them makes little to no sense to me.
Start from here:
http://www.codeguru.com/Csharp/Csharp/cs_collections/article.php/c5505/

This article shows implementation of collection class which supports foreach syntax. It enumerates array of points and returns point to client. Points is just sample, collection may expose database records, pixels etc. However, think about performance - it is critical in image processing.
In any case, this method (and also simple callback method which is described in my first post) doesnt allow to implement filters. You can do only pixel operations which can access only one pixel in iteration.
Thanks.I'll try to compile the article ( it's very poor, as it gives no indication to the file layout or what libraries to include and gives no 'running' example, but I'll try to wade through it anyway ) and see what I can figure out. I'll get back to you soon.
In any case, I beleive that callback approach is much faster (and easy for implementation). It gives the same effect - instead of writing foreach loop you write callback function and call ChangeBitmap giving delegate which points to this function. You can choose what you like.
Well the thing is I want to use DIFFERENT operations with each thing it runs. And I don't understand the callback method, or how it's at all useful. I'm not understanding what it's doing.
Also, I can't have any forms involved. None whatsoever.
I'm not quite sure what the callback does. Does it have to use events? The goal I'm trying to achieve is cleanliness of code. I've got about 12 different processes that use this code VERY similarly, and having them all together, while it works, is kind of messy, and I know that I could streamline this and make it much, much cleaner.
And also, what if I don't want to change the pixels, I just want to get their values, but I don't want to store the value information anywhere permanently. I'm sorry but callbacks are very, VERY new to me and I really don't understand them. I don't get what you're doing with the delegates, or how they are even working. Can you maybe explain that in a bit more detail and I can try it from that approach?
>> I don't understand the callback method.

Let's see it again. You want to do such thing:

foreach pixel in bitmap
     do something with pixel

Callback works by other way: write function

void DoSomethingWithPixel(pixel)
{
    ....
}

and pass it to Function ChangeBitmap (or ScanBitmap etc.). Give to ChangeBitmap address of DoSomethingWithPixel and it calls this function for each pixel in bitmap. If you want to do something else, write another function:

void DoSomethingElseWithPixel(pixel)
{
    ....
}

Call the same ChangeBitmap function and give to it address of DoSomethingElseWithPixel. Now DoSomethingElseWithPixel is called for each pixel in bitmap. ChangeBitmap is always the same, but different callback functions define current algorithm. I suggest you to create this sample and run it with two different callback functions to understand how it works. Windows form is used only to see results easy, image processing stuff doesn't depend on it.

Right ..so it isn't even driven. Okay! Sorry for the persistently numerous posts, force of habit. This is starting to make a little bit of sense.
So the Delegate accepts the Pointer to the Pixels within the Unsafe code and does stuff with it based on the Implementation of the Outside method? so will 'callback' return ONLY p[0]. What if I need it to return p, ( which consists of p[0],p[1],p[2], and p[3] ) but I don't want it to return it as unmanaged code. Just define a 4 dimensional int[,,,] array and have it return that?
>> And also, what if I don't want to change the pixels, I just want to get their values.

Instead of passing pixel by reference pass it by value:

callback(p[0]);


void DoSomethingWithPixel(byte pixel)
{
    ...      
}

Now you have a lot of information, you can think and decide.
ASKER CERTIFIED SOLUTION
Avatar of AlexFM
AlexFM

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
So a Delegate wraps a methods parameters and allows an entire method to be sent like a variable?
I didn't understand last post. Delegate is basically pointer to function. It is type-safe and may point only to function of the same type as delegate type. Delegate is initialized with some function, and this function may be called using delegate.
     I appear to still be doing something wrong ...


      delegate void ChangePixelDelegate(byte red, byte green, byte blue);

            void ChangePixel(byte red,byte green,byte blue)
            {
                  red -= 255;
                  green -= 255;
                  blue -= 255;
            }
            private void BtnClick(object sender,System.EventArgs e)
            {
                  m_bmp1 = new Bitmap(pictureBox1.Image);
                  m_bmp2 = new Bitmap(m_bmp1);

                  ChangeBitmap(m_bmp2, new ChangePixelDelegate(this.ChangePixel));
                  pictureBox2.Image = m_bmp2;
            }

            void ChangeBitmap(Bitmap bmImage, ChangePixelDelegate callback)
            {
                  // Lock the bits
                  BitmapData bmData = bmImage.LockBits
                        (new Rectangle(Point.Empty,bmImage.Size),
                        ImageLockMode.ReadWrite,PixelFormat.Format24bppRgb);

                  // Retrieve the Bitmap Stride Width
                  Int32 Stride = bmData.Stride;
                  // Create Unmanaged memory for this code
                  IntPtr Scan0 = bmData.Scan0;

                  unsafe
                  {
                        byte * p = (byte *)(void *)Scan0;

                        Int32 nOffset = Stride  - bmImage.Width * 3;
                        Int32 nWidth  = bmImage.Width;

                        for(int y = 0; y < bmImage.Height; ++y)
                        {
                              for(int x=0; x < nWidth; ++x )
                              {
                                    callback((byte)p[2], (byte)p[1], (byte)p[0]);   // bytes order is BGR
                                    p+=3;
                              }
                              p += nOffset;
                        }
                  }

                  // Unlock the bitmap
                  bmImage.UnlockBits(bmData);
            }
      }
I needed 'ref' on the delegate and the callback, because I was just getting the pixel data and changing the values I got, rather than changing the actual pixels. This is starting to make a lot of sense. Thanks again for your help!
For anyone reading in on this. Working solution is as follows ( compile as a class library )

using System;
using System.Drawing;
using System.Drawing.Imaging;

namespace GDISharp
{
      /// <summary>
      /// Defines a structure containing
      /// data about individual pixels of
      /// an image
      /// </summary>
      public struct PixelData
      {
            public byte red;
            public byte green;
            public byte blue;
            public int x;
            public int y;
      }

      public delegate void PixelDelegate(
      ref byte red, ref byte green, ref byte blue,
      int x, int y);

      /// <summary>
      /// Classes for Enumerating, Checking, Comparing,and
      /// modifying Bitmap Pixels
      /// </summary>
      public class Pixels
      {
            public static void GetPixels(Bitmap b_Image,PixelDelegate CallBack)
            {
                  // Lock the bits
                  BitmapData bmData = b_Image.LockBits
                        (new Rectangle(Point.Empty,b_Image.Size),
                        ImageLockMode.ReadWrite,PixelFormat.Format24bppRgb);

                  // Retrieve the Bitmap Stride Width
                  int Stride = bmData.Stride;
                  // Create Unmanaged memory for this code
                  IntPtr Scan0 = bmData.Scan0;

                  unsafe
                  {
                        byte * p = (byte *)(void *)Scan0;

                        int nOffset = Stride  - b_Image.Width * 3;
                        int nWidth  = b_Image.Width;

                        for(int y = 0; y < b_Image.Height; ++y)
                        {
                              for(int x=0; x < nWidth; ++x )
                              {
                                    // Format is BGR
                                    CallBack(
                                          ref (byte)p[2],
                                          ref (byte)p[1],
                                          ref (byte)p[0],
                                          x,y);
                        
                                    p+=3;
                              }
                              p += nOffset;
                        }
                  }

                  // Unlock the bitmap
                  b_Image.UnlockBits(bmData);
            }
      }
}
Hey, this method works good, but I've noticed that it's still slow, even with the unmanaged code. Any reasons why that might be?
This is a price we always pay for nice code. Code which performs good is usually ugly and unreadable.
The problem here is function call inside of loop. You may improve something here reducing number of parameters. For example, instead of passing 3 pixels by reference, pass unsafe pointer to first pixel (callback function should be unsafe as well). Don't pass x, y - manage them in caller which can increment his own counters - as always, ugly but faster.
Unfortunately, the fastest way is not using of callback and enumerators.
I see. I did some checking and the checking algorithm doesn't seem to be what's slowing down my program ( tried it with the sorting off, then on, and noticed no difference in image loading ).

The purpose of this is that my OCR Program will scan documents ( quite quickly ), namely invoices. We're going to use a blank page between invoices ( because each invoice has ticket data/etc packed with it ) so that we can have a dilimiter scan to mark the end/beginning of invoices. This particular check just checks to see if a page is blank.
Not really an answer on your question but an other way of doing it.

Seeing the purpose of your program. I did something similar in the past.
I compressed the bitmap to a gif or jpg and checked its size.
An empty page compresses dramatically to a few hundred bytes.
As i was scanning pictures separated by an empty picture it was easy to check only the size.
And it was really quick.
Don't know if you can use it for your application. I didn't tried it on text scans.


How did you check the size?
Yes, pictures seperated by empty pages. That's exactly what I'm doing. Right. Your idea might work. The images are about 300 KB each ( pretty consistent ) whereas the blank pages are about 9 KB.
Okay.I just scanned 50 ( it's an insanely fast scanner ) test documents. ALL of the documents were listed at 374 KB. And all of the blank pages were 9 KB. If this consistency is good, ( and it's a decent margin ), it may work. I just need to figure out how to check the file size.
Hrnm.The FileInfo class would work for images with filenames ...and for images that don't yet have filenames, I've already got the algorithm to get the filesize of an image. Yes! This will work! I can't believe I never thought of it before. It'd be SO Much faster too!
https://www.experts-exchange.com/questions/20929014/For-Extended-Assistance-with-Q-20919696.html

Go here KhunJean,  and post your answer so that it can be visible to anyone wanting to see it.
Derek,

Remeber i did this a long time ago. Not using C#.
I just checked the filesize.

But something like this can do the trick. If you already have a bitmap in memory.

The original bitmap stored in MyBitmap. Compress it with:

MyBitmap.Save(MemStream, System.Drawing.Imaging.ImageFormat.Png)

and check for its size with

MemStream.Length.

If it is under a certain size you have your seperator bitmap.

I scanned pictures. Complete blank CLEAN pictures had an avarage size of 1.5 Kb.
Pictures with some smudges had about 8-10Kb.
Because all the other pictures had a size bigger than 100Kb it was an easy test.