0% found this document useful (0 votes)
165 views

Image Processing For Dummies With C# and GDI+ Part 1

Uploaded by

Sanket Swarup
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
165 views

Image Processing For Dummies With C# and GDI+ Part 1

Uploaded by

Sanket Swarup
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 12

11/21/22, 9:52 PM Image Processing for Dummies with C# and GDI+ Part 1 - Per Pixel Filters - CodeProject

15,493,018 members ☰
747

articles Q&A forums stuff lounge ? Search for articles, questions,

Watch

Image Processing for Dummies with


C# and GDI+ Part 1 - Per Pixel Filters
Christian Graus Rate me: 4.91/5 (200 votes)

21 Mar 2002

The first in a series of articles which will build an image processing library in C# and GDI+

Is your email address OK? You are signed up for our newsletters or notifications but
your email address is either unconfirmed, or has not been reconfirmed in a long
time. Please click here to have a confirmation email sent so we can start sending
you newsletters again.

Download demo project - 7.41 Kb


Download source - 21.2 Kb

https://www.codeproject.com/Articles/1989/Image-Processing-for-Dummies-with-C-and-GDI-Part-1 1/12
11/21/22, 9:52 PM Image Processing for Dummies with C# and GDI+ Part 1 - Per Pixel Filters - CodeProject

Welcome to this, my first article in C#, and the first in a series on image processing. I figure
between Nish and Chris Losinger waiting to bust my chops, I should learn as much as anyone from
this article.

Overview
The purpose of the series will be to build a class that allows any C# programmer access to
common, and not so common, image processing functionality. The reason we are doing it in C# is
simply that I want to learn it, but the functionality we use is available through GDI+ in C++, and
indeed the code to do the same thing using a DIBSECTION is not terribly different. This first article
will focus on per pixel filters, in other words, filters that apply the same algorithm to each pixel 'in
place' with no regard for the values in any other pixels. You will see as we progress that the code
becomes somewhat more complex when we start moving pixels or changing values based on
calculations that take into account surrounding pixel values.

The App
The app we will use is a basic Windows Forms application ( it is in fact my first ). I've included code
to load and save images using GDI+, and a menu to which I will add filters. The filters are all static
functions in a class called BitmapFilter, so that an image can be passed in ( C# passes complex
types in by reference ) and a bool returned to indicate success or failure. As the series progresses I
am sure the app will get some other nice functionality, such as scaling and warping, but that will
probably happen as the focus of an article after the core functionality is in place. Scrolling is
achieved in the standard manner, the Paint method uses the AutoScrollPosition
property to
find out our scroll position, which is set by using the AutoScrollMinSize
property. Zooming is
achieved through a double, which we set whenever we change the scale, and which is used to set
the AutoScrollMinSize
anew, as well as to scale the Rectangle we pass into DrawImage
in the
Paint method.

Pixel Access, a.k.a. Unsafe code, and other nastiness


My first real disappointment in building this code was to find that the BitmapData
class in GDI+
does not allow us to access the data it stores, except through a pointer. This means we need to use
the unsafe keyword to scope the block of code which accesses the data. The net effect of this is
that a highly security level is required for our code to execute, i.e. any code using the BitmapData
class is not likely to be run from a remote client. This is not an issue for us right now, though, and it
is our only viable option, as GetPixel/SetPixel is simply too slow for us to use iterating through
bitmaps of any real size.

The other down side is that this class is meant to be portable, but anyone using it will need to
change their project settings to support compiling of unsafe code.

A quirk I noticed from the first beta of GDI+ continues to this day, namely requesting a 24bitRGB
image will return a 24bitBGR image. BGR ( that is, pixels are stored as blue, green, red values ) is

https://www.codeproject.com/Articles/1989/Image-Processing-for-Dummies-with-C-and-GDI-Part-1 2/12
11/21/22, 9:52 PM Image Processing for Dummies with C# and GDI+ Part 1 - Per Pixel Filters - CodeProject

the way Windows stored things internally, but I'm sure more than a few people will get a surprise
when they first use this function and realise they are not getting what they asked for.

Invert Filter
Here, then is our first, and most simple filter - it simply inverts a bitmap, meaning that we subtract
each pixel value from 255.

C# Shrink ▲  

public static bool Invert(Bitmap b)

// GDI+ still lies to us - the return format is BGR, NOT RGB.


BitmapData bmData = b.LockBits(new Rectangle(0, 0, b.Width, b.Height),

ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);

int stride = bmData.Stride;

System.IntPtr Scan0 = bmData.Scan0;

unsafe

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

int nOffset = stride - b.Width*3;

int nWidth = b.Width * 3;

for(int y=0;y < b.Height;++y)

for(int x=0; x < nWidth; ++x )

p[0] = (byte)(255-p[0]);

++p;

p += nOffset;

b.UnlockBits(bmData);

return true;

This example is so simple that it doesn't even matter that the pixels are out of order. The stride
member tells us how wide a single line is, and the Scan0 member is the pointer to the data. Within
our unsafe block we grab the pointer, and calculate our offset. All bitmaps are word aligned, and
so there can be a difference between the size of a row and the number of pixels in it. This padding
must be skipped, if we try and access it we will not simply fail, we will crash. We therefore calculate
the offset we need to jump at the end of each row and store it as nOffset.

The key thing when image processing is to do as much outside the loop as possible. An image of
1024x768 will contain 786432 individual pixels, a lot of extra overhead if we add a function call, or
create a variable inside the loops. In this case, our x loop steps through Width*3 iterations, when
we care about each individual color, we will step the width only, and increment our pointer by 3 for
each pixel.

That should leave the rest of the code pretty straightforward. We are stepping through each pixel,
and reversing it, as you can see here:
https://www.codeproject.com/Articles/1989/Image-Processing-for-Dummies-with-C-and-GDI-Part-1 3/12
11/21/22, 9:52 PM Image Processing for Dummies with C# and GDI+ Part 1 - Per Pixel Filters - CodeProject

  

Grayscale filter
Subsequent examples will show less and less of the code, as you become more familiar with what
the boilerplate part of it does. The next, obvious filter is a grayscale filter. You might think that this
would involve simply summing the three color values and dividing by three, but this does not take
into effect the degree to which our eyes are sensitive to different colors. The correct balance is
used in the following code:

C# Shrink ▲  

unsafe

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

int nOffset = stride - b.Width*3;

byte red, green, blue;

for(int y=0;y < b.Height;++y)

for(int x=0; x < b.Width; ++x )

blue = p[0];

green = p[1];

red = p[2];

p[0] = p[1] = p[2] = (byte)(.299 * red

+ .587 * green

+ .114 * blue);

p += 3;

p += nOffset;

As you can see, we are now iterating through the row b.Width times, and stepping through the
pointer in increments of 3, extracting the red, green and blue values individually. Recall that we are
pulling out bgr values, not rgb. Then we apply our formula to turn them into the grey value, which
obvious is the same for red, green and blue. The end result looks like this:
https://www.codeproject.com/Articles/1989/Image-Processing-for-Dummies-with-C-and-GDI-Part-1 4/12
11/21/22, 9:52 PM Image Processing for Dummies with C# and GDI+ Part 1 - Per Pixel Filters - CodeProject

  

A note on the effects of filters


It's worthwhile observing before we continue that the Invert filter is the only non-destructive filter
we will look at. That is to say, the grayscale filter obviously discards information, so that the original
bitmap cannot be reconstructed from the data that remains. The same is also true as we move into
filters which take parameters. Doing a Brightness filter of 100, and then of -100 will not result in
the original image - we will lose contrast. The reason for that is that the values are clamped - the
Brightness filter adds a value to each pixel, and if we go over 255 or below 0 the value is adjusted
accordingly and so the difference between pixels that have been moved to a boundary is
discarded.

Brightness filter
Having said that, the actual filter is pretty simple, based on what we already know:

C#
for(int y=0;y<b.Height;++y)

for (int x = 0; x < nWidth; ++x)

nVal = (int) (p[0] + nBrightness);

if (nVal < 0) nVal = 0;

if (nVal > 255) nVal = 255;

p[0] = (byte)nVal;

++p;

p += nOffset;

The two examples below use the values of 50 and -50 respectively, both on the original image

https://www.codeproject.com/Articles/1989/Image-Processing-for-Dummies-with-C-and-GDI-Part-1 5/12
11/21/22, 9:52 PM Image Processing for Dummies with C# and GDI+ Part 1 - Per Pixel Filters - CodeProject

    

Contrast
The operation of contrast is certainly the most complex we have attempted. Instead of just moving
all the pixels in the same direction, we must either increase or decrease the difference between
groups of pixels. We accept values between -100 and 100, but we turn these into a double
between the values of 0 and 4.

C#
if (nContrast < -100) return false;

if (nContrast > 100) return false;

double pixel = 0, contrast = (100.0+nContrast)/100.0;

contrast *= contrast;

My policy has been to return false when invalid values are passed in, rather than clamp them,
because they may be the result of a typo, and therefore clamping may not represent what is
wanted, and also so users can find out what values are valid, and thus have a realistic expectation
of what result a given value might give.

Our loop treats each color in the one iteration, although it's not necessary in this case to do it that
way.

C#
https://www.codeproject.com/Articles/1989/Image-Processing-for-Dummies-with-C-and-GDI-Part-1 6/12
11/21/22, 9:52 PM Image Processing for Dummies with C# and GDI+ Part 1 - Per Pixel Filters - CodeProject

red = p[2];

pixel = red/255.0;

pixel -= 0.5;

pixel *= contrast;

pixel += 0.5;

pixel *= 255;

if (pixel < 0) pixel = 0;

if (pixel > 255) pixel = 255;

p[2] = (byte) pixel;

We turn the pixel into a value between 0 and 1, and subtract .5. The net result is a negative value
for pixels that should be darkened, and positive for values we want to lighten. We multiply this
value by our contrast value, then reverse the process. Finally we clamp the result to make sure it is
a valid color value. The following images use contrast values of 30 and -30 respectively.

     

Gamma
First of all, an explanation of this filter. The following explanation of gamma was found on the web:
In the early days of television it was discovered that CRT's do not produce a light intensity that is
proportional to the input voltage. Instead, the intensity produced by a CRT is proportional to the
input voltage raised to the power gamma. The value of gamma
varies depending on the CRT, but
is usually close to 2.5. The gamma response of a CRT is caused by electrostatic effects in the
electron gun. In other words, the blue on my screen might well not be the same as the blue on
your screen. A gamma filter attempts to correct for this. It does this by building a gamma ramp, an
https://www.codeproject.com/Articles/1989/Image-Processing-for-Dummies-with-C-and-GDI-Part-1 7/12
11/21/22, 9:52 PM Image Processing for Dummies with C# and GDI+ Part 1 - Per Pixel Filters - CodeProject

array of 256 values for red, green and blue based on the gamma value passed in (between .2 and
5). The array is built like this:

C#
byte [] redGamma = new byte [256];

byte [] greenGamma = new byte [256];

byte [] blueGamma = new byte [256];

for (int i = 0; i < 256; ++i)

redGamma[i] = (byte)Math.Min(255, (int)(( 255.0

* Math.Pow(i/255.0, 1.0/red)) + 0.5));

greenGamma[i] = (byte)Math.Min(255, (int)(( 255.0

* Math.Pow(i/255.0, 1.0/green)) + 0.5));

blueGamma[i] = (byte)Math.Min(255, (int)(( 255.0

* Math.Pow(i/255.0, 1.0/blue)) + 0.5));

You'll note at this point in development I found the Math class.

Having built this ramp, we step through our image, and set our values to the values stored at the
indices in the array. For example, if a red value is 5, it will be set to redGamma[5]. The code to
perform this operation is self evident, I'll jump right to the examples. I've used Gamma values of .6
and 3 for the two examples, with the original as always first for comparison. I used the same values
for red, green and blue, but the filter allows them to differ.

  

https://www.codeproject.com/Articles/1989/Image-Processing-for-Dummies-with-C-and-GDI-Part-1 8/12
11/21/22, 9:52 PM Image Processing for Dummies with C# and GDI+ Part 1 - Per Pixel Filters - CodeProject

Color Filter
Our last filter is a color filter. It is very simple - it just adds or subracts a value to each color. The
most useful thing to do with this filter is to set two colors to -255 in order to strip them and see
one color component of an image. I imagine by now you'd know exactly what that code will look
like, so I'll give you the red, green and blue components of my son to finish with. I hope you found
this article informative, the next will cover convolution filters, such as edge detection, smoothing,
sharpening, simple embossing, etc. See you then !!!

     

This article is part of the series 'Image Processing for Dummies with C# and GDI+ Vi… Next ▷

License
This article has no explicit license attached to it but may contain usage terms in the article text or
the download files themselves. If in doubt please contact the author via the discussion board
below.

A list of licenses authors might use can be found here

Written By

https://www.codeproject.com/Articles/1989/Image-Processing-for-Dummies-with-C-and-GDI-Part-1 9/12
11/21/22, 9:52 PM Image Processing for Dummies with C# and GDI+ Part 1 - Per Pixel Filters - CodeProject

Christian Graus
Software Developer (Senior)
 Australia

Programming computers ( self taught ) since about 1984 when I bought my first Apple ][. Was
working on a GUI library to interface Win32 to Python, and writing graphics filters in my spare time,
and then building n-tiered apps using asp, atl and asp.net in my job at Dytech. After 4 years there,
I've started working from home, at first for Code Project and now for a vet telemedicine company. I
owned part of a company that sells client education software in the vet market, but we sold that
and I worked for the owners for five years before leaving to get away from the travel, and spend
more time with my family. I now work for a company here in Hobart, doing all sorts of Microsoft
based stuff in C++ and C#, with a lot of T-SQL in the mix.

Watch

Comments and Discussions


 

Add a Comment or Question    Email Alerts Search Comments

First Prev Next

Dirty Rectangle Algorithm


Member 10938000 23-Aug-17 19:10

My vote of 5
DrABELL 31-Aug-15 1:36

Gray Scale to 1-Bit


Railroadguy 5-Aug-15 21:22

Voted 5
Sivaraman Dhamodharan 16-May-15 22:32

image processing with c#


jackmay2 12-Dec-14 8:06

Re: image processing with c#


Christian Graus 12-Dec-14 8:16

License
Gonger96 7-Jan-14 3:07

Re: License
Christian Graus 7-Jan-14 3:10

https://www.codeproject.com/Articles/1989/Image-Processing-for-Dummies-with-C-and-GDI-Part-1 10/12
11/21/22, 9:52 PM Image Processing for Dummies with C# and GDI+ Part 1 - Per Pixel Filters - CodeProject

Re: License
Gonger96 7-Jan-14 14:41

OpenFile Dialog
Member 10391911 21-Nov-13 21:42

Re: OpenFile Dialog


Christian Graus 23-Nov-13 13:47

Re: OpenFile Dialog


Member 10391911 24-Nov-13 12:39

Problem with Flipping bitmap


Mehran Davidi 15-Mar-13 19:20

Re: Problem with Flipping bitmap


Christian Graus 15-Mar-13 19:31

Re: Problem with Flipping bitmap


Vishal Makwana 8-Apr-13 1:10

Slow Processing Speed when using Large Images


Member 8216740 19-Jun-12 13:30

Re: Slow Processing Speed when using Large Images


Christian Graus 19-Jun-12 13:34

How can you count the percent of pixels for some color?
alexandlu 15-May-12 21:12

Re: How can you count the percent of pixels for some color?
Christian Graus 16-May-12 17:18

Thanks for the great article & asking for more


muratcantuna 15-Apr-12 20:32

Re: Thanks for the great article & asking for more
Christian Graus 16-Apr-12 5:13

是否给出插入文字的方法
waphack 4-Mar-12 14:33

My vote of 5
VHGN 29-Feb-12 23:33

Re: My vote of 5
Christian Graus 19-Jun-12 13:35

Re: My vote of 5
VHGN 19-Jun-12 14:06

Refresh 1 2 3 4 5 6 7 8 9 10 11 Next ᐅ

https://www.codeproject.com/Articles/1989/Image-Processing-for-Dummies-with-C-and-GDI-Part-1 11/12
11/21/22, 9:52 PM Image Processing for Dummies with C# and GDI+ Part 1 - Per Pixel Filters - CodeProject

General    News    Suggestion    Question    Bug    Answer    Joke    Praise    Rant   


Admin   

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

Permalink
Layout: fixed
|
fluid Article Copyright 2002 by Christian
Advertise
Graus
Privacy
Everything else
Copyright ©
Cookies
CodeProject, 1999-2022
Terms of Use

Web01 2.8:2022-11-08:1

https://www.codeproject.com/Articles/1989/Image-Processing-for-Dummies-with-C-and-GDI-Part-1 12/12

You might also like