If you use Bitmap class of c# to access individual pixels in the bitmap, you probably know that GetPixel() and SetPixel() are too slow and unsuitable for image processing. For fast image processing in c#, It is better to use the FastBitmap
class. FastBitmap
allows us to access the raw memory of bitmap data via pointers, and therefore it is more efficient and suitable for image processing.
FastBitmap For Fast Image Processing in C#
FastBitmap Constructor
The constructor takes the bitmap we want to process and an optional rectangle of the bitmap we want to access. It will lock the raw memory of the bitmap and will allow us to access the raw memory. As the FastBitmap
implements the IDisposable Interface – The Dispose
method should be called to unlock the memory and free the unmanaged resources when we finish processing the bitmap.
FastBitmap Properties
Let’s see how are the properties the FastBitmap
allows to locate individual pixels in the bitmap.
- The
PixelSize
is the the size of a pixel in bytes. - The
Stride
is the width in bytes of a row of pixels of the image. - The
Scan0
is the pointer to the left top pixel of the rectangle we want to process. - The
XX
andYY
are the left and top of the rectangle of the bitmap we want to process. - The
Width
andHeight
are the width and height of the rectangle of the bitmap we want to process.
How to Access The Raw Memory Of A Pixel?
Let’s see how to access the raw memory of the pixel (xx, yy) in the rectangle of the bitmap we want to process.
Suppose the bitmap we want to process a bitmap is a color image in 24bpp-BGR format. The bitmap’s dimensions are 9×6 dimensions, and we want to process the rectangle with 4×3 dimensions where the left top pixel is located ar (3,1). We can write the following code:
FastBitmap Constructor and Properties
fb = new FastBitmap(bitmap,3,1,4,3);
...
fb.Dispose()
![Find the first byte of a pixel [How to Access the raw memory of the bitmap?]](https://napuzba.com/wp-content/uploads/2021/06/fast-image-processing-in-c-1.jpg)
As we can see in the above diagram and code, the properties of the FastBimap instance we created are:
- The
fb.PixelSize
is 3 ( Each pixel has 3 bytes) - The
fb.Stride
isPixelSize
*bitmap's width
= 3 * 9 = 27 fb.XX
= 3 andfb.YY
= 1 since the left top of the rectangle we want to process is located at(3,1
)fb.Width
= 4 andfb.Height
= 3 since the dimensions of the rectangle we want to process is4x3
- The
fb.scan0
points to the first byte of the pixel(3,1)
– The left top of the rectangle we want to process
Now , Using those values we can access easily find the first byte of the pixel (xx,yy) in the rectangle we want to process
Find the first byte of a pixel
first_byte_of_pixel = fb.Scan0 + yy * fb.Stride + xx * fb.PixelSize
Demo For Fast Image Processing in c#
The following demo replaces the center of an image with its grey version of it. We leverage the FastBitmap class in this demo.
Image processing in c# – FastBitmap approach
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 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 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 newly 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 allow 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 initialized 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)
calculation for converting color to color in grey scale
Demo For Fast Image Processing in c# In Action
Running the demo program on the left image will produce the right image:


Benchmark: FastBitmap vs opencv vs Bitmap getPixel/setPixel
Some readers suggested to measure 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
library.
Image processing in c# – 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.
Image processing in c# – Bitmap GetPixel and SetPixel approach
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.
Image Processing with c++ – The opencv approach
In this approach we will use C++ and opencv library. I tried to mimic the ideas of FastBitmap
class.
Image Processing with c++ – opencv approach
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
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.
Benchmark Results
The benchmark strategy was to run each program 1000 times with 800×600 jpg file and measure 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:

Bebchmark: c# with FastBitma vs c++ with opencv vs c# with Bitmap GetPixel and SetPixel.
As we can see:
- The
c#(FastBitmap and pointers)
approach improve the speed ofc#(Bitmap GetPixel and SetPixel)
approach by 80%. - It seems that the
C++(opencv)
approach have a negligible speed improvement fromc#(FastBitmap and pointers)
approach (less than 2% improvement).
Process Images in c# as Fast as c++
We see that with FastBitmap class, we can fast access the raw memory of bitmap data via pointers. Moreover, we see that we can process images with c# as Fast as c++ and OpenCV. Now, We can say fast image processing in c# is possible. There is no excuse why you should not use c# for image proceeding. You can undoubtedly apply known algorithms such as edge detection, histogram equalization, thresholding, and more, without any second thought about performance.
Try c++ Halide lang. It leaves fast bitmap and open cv in the dust…