Click here to Skip to main content
13,198,322 members (54,136 online)
Click here to Skip to main content
Add your own
alternative version

Tagged as

Stats

26.3K views
2K downloads
67 bookmarked
Posted 22 Feb 2016

Fast image processing in C#

, 21 Aug 2017
Rate this:
Please Sign up or sign in to vote.
Improving image processing speed by fast accessing the raw memory of bitmap data via pointers.

Introduction

While the Bitmap provide GetPixel() and SetPixel() for accessing individual pixels in the bitmap - It is too slow and therefore it not suitable to image processing. The following FastBitmap class allows us to fast access the raw memory of bitmap data via pointers.

About FastBitmap

The FastBitmap implement the IDisposable Interface. The constructor takes the bitmap we want to process and an optional rectangle of the bitmap we want to access. It will locks the raw memory of bitmap and will allow us to access the raw memory. The Dispose method should be called to unlock the memory and free the unmanaged resources when we done with processing the bitmap.

The class provides some properties which allows to locate individual pixels in the bitmap

  • PixelSize holds the size of pixel in bytes
  • Stride holds the width in bytes of row of pixels of the image
  • Scan0 holds a pointer to the location of the left top pixel of the rectangle we want to process.
  • XX,YY are the left and top of the rectangle of the bitmap we want to process.
  • Width,Height are the width and height of the rectangle of the bitmap we want to process.

For example:

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

Suppose the bitmap is color image in 24bpp-BGR:

  • The bitmap dimensions are 9x6
  • fb.PixelSize is 3 ( Each pixel has 3 bytes)
  • The fb.Stride is PixelSize * bitmap's width = 3 * 9 = 27
  • The rectangle we want to processed is located at (3,1)
    • fb.XX = 3
    • fb.YY = 1
  • The dimensions of the rectangle we want to processed is 4x3
    • fb.Width = 4
    • fb.Height= 3
  • The fb.scan0 will point to pixel located at (3,1).

Using those values we can access the pixel (xx,yy) in the rectangle by the following calculation:

  • 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) { // <== A1
    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)) { //  <== A2
    unsafe {                                                                                 //  <== A3
        byte* row = (byte*)fbitmap.Scan0, bb = row;                                          //  <== A4
        for (    int yy = 0; yy  < fbitmap.Height; yy++, bb  = (row += fbitmap.Stride)) {    //  <== A5
            for (int xx = 0; xx  < fbitmap.Width ; xx++, bb += fbitmap.PixelSize) {          //  <== A6
                // *(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);                              //  <== A7
                *(bb + 0) = *(bb + 1) = *(bb + 2) = gray;
            }
        }
    }
}    

In A1 we verify that the given image bitmap is color image in 24bpp-BGR or 32bpp-BGRA image format. Since those formats are similar, we can process them with same code :

  • In 24bpp-BGR format each pixel is stored in 24 bits (3 bytes) per pixel. Each pixel component 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)
  • 32bpp-BGRA format extends 24bpp-BGR format with an extra alpha component. In this format each pixel is stored in 32 bits(4 bytes) per pixel. Each pixel component 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)

In A2 we create the FastBitmap object with the given bitmap and the rectangle we want to process. 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). We are using the using block which will ensure invoking the Dispose method on the newlly created object at the end of the this block.

In A3 we start a unsafe since we are going to use pointers. Please note that we also need to compile with unsafe compiler flag.

In A4 we declare two pointers which allows us to access the pixels , pixel by pixel (top to down and left to right). The first byte pointer row point to the first pixel in the current row and initlized to to left top pixel of the rectangle we want to process. The second byte pointer bb will point to the current pixel.

In A5 we looping for each row in the rectangle and update the current pixel and row to first pixel in the current row using the Stride property.

In A6 we looping for each pixel in the current row and update the bb pointer using the PixelSize property.

In A7 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 calculation:

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

Running the demo program on the left image will produce the right image:

The orginal imageThe grey image

Benchmarks

Some readers suggested to mesuare the performance against other image processing alternatives. I have created 2 programs which implement the functionality of the above demo program using other approaches. The first will use Bitmap GetPixel and SetPixel while the other will use C++ and opencv.

Bitmap GetPixel and SetPixel approach

The program is very similar to the orginal demo program. However, instead of using pointers, we will access the pixels using GetPixel and SetPixel methods of the Bitmap class.

if (bitmap.PixelFormat != PixelFormat.Format24bppRgb && bitmap.PixelFormat != PixelFormat.Format32bppArgb) { // <== A1
    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++) {            // <== A1
    for (int xx = x1; xx < xn; xx++) { 
        cc = bitmap.GetPixel(xx, yy);             // <== A2
        byte gg = (byte)((cc.B * 1140 + 
                          cc.G * 5870 + 
                          cc.R * 2989) / 10000);  // <== A3
        gray = Color.FromArgb( gg,gg,gg); 
        bitmap.SetPixel(xx, yy, gray);            // <== A4
    }
}    

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

In A2 we getting the Color of the current pixel using GetPixel method.

In A3 we create grey Color by setting its components to the above grey level calculation.

In A4 we setting the current pixel to this grey color using SetPixel method.

C++ and opencv approach

In this approach we will use C++ and opencv library. I tried to mimic the ideas of FastBitmap class.

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

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

uchar* row = scan0, *bb = row;                      // <== A4
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);       // <== A5    
</uchar>

In A1 we load the image data from file to memory.

In A2 we find the rectangle we want to process

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

The image processing code in A4 is the same code from our first c# programs. It seems that in the pointers syntax c++ and c# are on the same page. Some will say that c# is standing on the shoulders of giants.

In A5 we write the image to file.

Benchmarks

The benchmark strategy was to run each program 1000 times with 800x600 jpg file and mesuare the average time each program last in system ticks (1 millisecond is 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 have a negligible speed improvement from c#(FastBitmap and pointers) approach (less than 2% improvement).

Summery

Using FastBitmap class, we can fastly access the raw memory of bitmap data via pointers. It allows us to do image processing in c# and apply known algorithms such as edge detection, histogram equalization, thresholding and more.

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
Pro

Comments and Discussions

 
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 | Terms of Use | Mobile
Web04 | 2.8.171020.1 | Last Updated 22 Aug 2017
Article Copyright 2016 by napuzba
Everything else Copyright © CodeProject, 1999-2017
Layout: fixed | fluid