Click here to Skip to main content
13,633,747 members
Click here to Skip to main content
Add your own
alternative version

Tagged as

Stats

40.8K views
2.9K downloads
82 bookmarked
Posted 22 Feb 2016
Licenced CPOL

Image Processing and Pointers

, 1 Apr 2018
Rate this:
Please Sign up or sign in to vote.
How pointers to the raw memory of bitmap data can improve the image processing speed

Introduction

If you use Bitmap class and its methods GetPixel() and SetPixel() for accessing bitmap's pixels - you can increase the speed of the image processing with the following FastBitmap class.

About FastBitmap

The FastBitmap allows to access the raw memory of bitmap data. For convenience, the class implements IDisposable Interface which allows to easily manage the unmanaged raw memory data.

The class allows to process a rectangle in the bitmap. We can use the following properties to locate pixel in this rectangle:

  • YY,XX are the top left point of the rectangle
  • Width, Height are the dimensions of the rectangle
  • PixelSize is size of pixel in bytes
  • Scan0 is a pointer to the location of the top left pixel of the rectangle
  • Stride is the width in bytes of row of pixels of the image

The constructor locks the raw memory of bitmap and allows us to access it via pointers. It takes the bitmap and an optional rectangle we want to proccess.  The Dispose frees the unmanaged resources and unlocks the memory - You should call it when you finish to process the bitmap.

For example:

fb = new FastBitmap(bitmap,3,1,4,3);
...
fb.Dispose()    

Suppose the bitmap is color image in 24bpp-BGR and its dimensions are 9x6.

  • fb.PixelSize is 3 since each pixel has 3 bytes.
  • We process the 4x3 rectangle located at (3,1)  therefore fb.Width=4, fb.Height=3, fb.XX=3 , fb.YY=1
  • The fb.Stride is fb.PixelSize * bitmap's width so fb.Stride=9*3=27 
  • The fb.scan0 will point to pixel located at (3,1).

To access pixel (xx,yy) in the rectangle, we use the following forumla:

Location(xx,yy) = fb.Scan0 + yy * fb.Stride + xx * fb.PixelSize

Using FastBitmap

The following demo shows how can we use this class - The demo will replace the center of an image with its grey version of it.

if (bitmap.PixelFormat != PixelFormat.Format24bppRgb && 
bitmap.PixelFormat != PixelFormat.Format32bppArgb) { // <== Z1
    return;
}
int ww = bitmap.Width  / 8;
int hh = bitmap.Height / 8;
using (FastBitmap fbitmap = new FastBitmap(bitmap, ww, hh, 
                                           bitmap.Width - 2 * ww, bitmap.Height - 2 * hh)) { //  <== Z2
    unsafe {                                                                                 //  <== Z3
        byte* row = (byte*)fbitmap.Scan0, bb = row;                                          //  <== Z4
        for (    int yy = 0; yy  < fbitmap.Height; yy++, bb  = (row += fbitmap.Stride)) {    //  <== Z5
            for (int xx = 0; xx  < fbitmap.Width ; xx++, bb += fbitmap.PixelSize) {          //  <== Z6
                // *(bb + 0) is B (Blue ) component of the pixel
                // *(bb + 1) is G (Green) component of the pixel
                // *(bb + 2) is R (Red  ) component of the pixel
                // *(bb + 3) is A (Alpha) component of the pixel ( for 32bpp )
                byte gray = (byte)((1140 * *(bb + 0) +
                                    5870 * *(bb + 1) + 
                                    2989 * *(bb + 2)) / 10000);                              //  <== Z7
                *(bb + 0) = *(bb + 1) = *(bb + 2) = gray;
            }
        }
    }
}    

In Z1, we check that the bitmap format is color image in 24bpp-BGR or 32bpp-BGRA format.

  • In 24bpp-BGR format, each pixel is stored in 24 bits (3 bytes) per pixel. Each pixel component is stored in 8 bits(1 byte) in the following order:
    • B component of the pixel is stored in byte 0 (Blue)
    • G component of the pixel is stored in byte 1 (Green)
    • R component of the pixel is stored in byte 2 (Red)
  • In 32bpp-BGR format, each pixel is stored in 32 bits(4 bytes) per pixel. Each pixel component is stored in 8 bits(1 byte) in the following order:
    • B component of the pixel is stored in byte 0 (Blue)
    • G component of the pixel is stored in byte 1 (Green)
    • R component of the pixel is stored in byte 2 (Red)
    • A component of the pixel is stored in byte 3 (Alpha)

We can see the 32bpp-BGRA format extends 24bpp-BGR format with an extra alpha component. We can use this observation to process those images with the same code.

In Z2, we are using the using block. This block ensures invoking the Dispose method on the FastBitmap object, we create in the beginning of the block, at the end of this block. In this case, the rectangle width and height is 3/4 of the bitmap and its left top pixel is located (1/8 of bitmap width, 1/8 of bitmap height).

In Z3, we start a unsafe block which allows us to use pointers inside it. Please note that we also need to compile with unsafe compiler flag.

In Z4, we declare two byte pointers. Those pointers will be used to access the pixels, pixel by pixel.

  • row points to the first pixel in the current row
  • bb points to the current pixel.

In Z5, we loop for each row (top to bottom) and updates row pointer to point to the first pixel in the current row (Note the use of the Stride property).

In Z6, we loop for each pixel in the current row and update the bb pointer (Note the use of the PixelSize property).

In Z7, we process the pixel using the current pixel pointed by bb.

In this demo, we convert the color of the pixel to grey scale color by setting all pixel’s components to the value of the following formula:

  • 0.1140 * (Blue component) + 0.5870 * (Green component) + 0.2989 * (Red component)

Let's see how we the demo works on the left image and produces the right one:

The original image The grey image

Benchmarks

Some readers suggested to measure the performance against other image processing alternatives.

Bitmap GetPixel and SetPixel Approach

The demo is similar to the original demo program. But, instead of using pointers, we will use SetPixel and GetPixel methods of the Bitmap class.

if (bitmap.PixelFormat != PixelFormat.Format24bppRgb && 
bitmap.PixelFormat != PixelFormat.Format32bppArgb) { 
    return;
}
int w0 = bitmap.Width  / 8; 
int h0 = bitmap.Height / 8;
int x1 = w0;
int y1 = h0;
int xn = x1 + bitmap.Width  - 2 * w0;
int yn = y1 + bitmap.Height - 2 * h0; 

Color gray, cc;
for (    int yy = y1; yy < yn; yy++) {            // <== Z1
    for (int xx = x1; xx < xn; xx++) { 
        cc = bitmap.GetPixel(xx, yy);             // <== Z2
        byte gg = (byte)((cc.B * 1140 + 
                          cc.G * 5870 + 
                          cc.R * 2989) / 10000);  // <== Z3
        gray = Color.FromArgb( gg,gg,gg); 
        bitmap.SetPixel(xx, yy, gray);            // <== Z4
    }
}  

In Z1, we loop over all the pixels’ locations of the rectangle, pixel by pixel (top to down and left to right).

In Z2, we use GetPixel method to to get the Color of the current pixel.

In Z3, we create grey Color using the by setting all its components to the above grey level formula.

In Z4, we use SetPixel method to set the pixel to grey Color , we found in the above step.

C++ and opencv Approach

The demo is similar to the original demo program, But instead of using pointers, we will use C++ and opencv library. I tried to follow the ideas of FastBitmap class.

Mat bitmap = imread(argv[1], CV_LOAD_IMAGE_COLOR);  // <== Z1
... 
int ww = bitmap.cols - 2 * bitmap.cols / 8;         // <== Z2
int hh = bitmap.rows - 2 * bitmap.rows / 8;
int x1 = bitmap.cols / 8;
int y1 = bitmap.rows / 8;

int pixelSize = bitmap.channels();                  // <== Z3
int stride = pixelSize * bitmap.cols;
uchar* scan0 = bitmap.ptr<uchar>(0) + (y1  * stride) + x1 * pixelSize;

uchar* row = scan0, *bb = row;                      // <== Z4
for (    int yy = 0; yy < hh; yy++, bb = (row += stride)) { 
    for (int xx = 0; xx < ww; xx++, bb += pixelSize     ) {
        // *(bb + 0) is B (Blue ) component of the pixel
        // *(bb + 1) is G (Green) component of the pixel
        // *(bb + 2) is R (Red  ) component of the pixel
        // *(bb + 3) is A (Alpha) component of the pixel ( for 32bpp )
        uchar gray = ((1140 * *(bb + 0) + 
                       5870 * *(bb + 1) + 
                       2989 * *(bb + 2)) / 10000);
        *(bb + 0) = *(bb + 1) = *(bb + 2) = gray;
    }
}
...
imwrite(argv[2], bitmap, compression_params);       // <== Z5    
</uchar>

In Z1, we load the image data from file to memory.

In Z2, we find the rectangle we want to process

In Z3, we find scan0, pixelSize and the stride of the bitmap

The image processing code in Z4 is the same code from our first C# programs.

In Z5, we write the image to file.

Benchmarks

I ran each program 1000 times with jpg image 800x600 and measure the average time each program last in system ticks (1 millisecond = 10000 ticks).

When I run the benchmark on my computer, Intel Core i7(6700K) machine, I get the following results:

As we can see:

  • The C#(FastBitmap and pointers) approach improve the speed of C#(Bitmap GetPixel and SetPixel) approach by 80%.
  • It seems that the C++(opencv) approach has a negligible speed improvement (less than 2% improvement) from C#(FastBitmap and pointers) approach

History

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

napuzba
Israel Israel
I am kobi, a passionate software developer.
Actively seeking for new opportunities.
Please feel free to contact me via my blog.

  • I'm working in C#, C++, PHP and Python
  • I'm building websites, mobile applications and desktop software.

You may also be interested in...

Pro

Comments and Discussions

 
SuggestionAnother option: convert bitmap to managed C# array without individual GetPixel calls?? Pin
wim4you2-Apr-18 2:22
memberwim4you2-Apr-18 2:22 
QuestionDouble the speed Pin
Tomaž Štih21-Jun-16 2:58
memberTomaž Štih21-Jun-16 2:58 
GeneralMy vote of 5 Pin
Mr. Lai Tai-Yu (Averill, Lai) (赖岱佑) (賴岱佑)25-Apr-16 6:32
professionalMr. Lai Tai-Yu (Averill, Lai) (赖岱佑) (賴岱佑)25-Apr-16 6:32 
QuestionYou really used maximum in C# but still missing main C++ advantages Pin
Alexey KK8-Mar-16 7:13
professionalAlexey KK8-Mar-16 7:13 
QuestionSpeed Improvement ? Pin
Peter Huber SG1-Mar-16 0:50
memberPeter Huber SG1-Mar-16 0:50 
AnswerI have added benchmark for Bitmap GetPixel and SetPixel Pin
napuzba6-Mar-16 23:44
membernapuzba6-Mar-16 23:44 
QuestionNice code. Pin
ma_ma24-Feb-16 10:25
memberma_ma24-Feb-16 10:25 
AnswerNot yet Pin
napuzba24-Feb-16 20:42
membernapuzba24-Feb-16 20:42 
AnswerI have added benchmark for C++ and OpenCV Pin
napuzba6-Mar-16 23:52
membernapuzba6-Mar-16 23:52 
GeneralRe: I have added benchmark for C++ and OpenCV Pin
ma_ma7-Mar-16 6:39
memberma_ma7-Mar-16 6:39 
AnswerRe: I have added benchmark for C++ and OpenCV Pin
napuzba7-Mar-16 8:19
membernapuzba7-Mar-16 8:19 
QuestionNice Code Pin
GuilhermeChristo24-Feb-16 8:04
memberGuilhermeChristo24-Feb-16 8:04 
AnswerYCbCr color space Pin
napuzba24-Feb-16 9:08
membernapuzba24-Feb-16 9:08 
QuestionHow fast? Pin
Kenneth Haugland23-Feb-16 2:02
professionalKenneth Haugland23-Feb-16 2:02 
AnswerFast enough for my purposes :) Pin
napuzba23-Feb-16 3:31
membernapuzba23-Feb-16 3:31 
GeneralRe: Fast enough for my purposes :) Pin
Kenneth Haugland23-Feb-16 4:00
professionalKenneth Haugland23-Feb-16 4:00 
Bugfast-image-processing-cs-src will not de-compress Pin
firmwaredsp22-Feb-16 20:49
memberfirmwaredsp22-Feb-16 20:49 
AnswerFixed Pin
napuzba22-Feb-16 21:33
membernapuzba22-Feb-16 21:33 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Permalink | Advertise | Privacy | Cookies | Terms of Use | Mobile
Web03-2016 | 2.8.180712.1 | Last Updated 1 Apr 2018
Article Copyright 2016 by napuzba
Everything else Copyright © CodeProject, 1999-2018
Layout: fixed | fluid