Click here to Skip to main content
15,867,686 members
Articles / Desktop Programming / WPF

Metro Style Lightweight Image Processing

Rate me:
Please Sign up or sign in to vote.
4.88/5 (48 votes)
31 Dec 2014CPOL6 min read 79.4K   4.2K   85   29
Sensor and image are two amazing things which interests programmer most in slate.

Introduction

Sensor and image are two amazing things which interest programmers most in slate and mobile computer. Using the two features, we can make a lot of amazing applications.

There are two main ways to do image processing, one is using direct X HLSL ,the other is using bitmap pixels. To write a casual game or APP with HDLS might be ‘the big topic does on a small scale’ for application developer. So that is the reason why I write this article.

Recently, I tried to port my image processing program written with GDI+ in the past to a Metro style application. The main part of my application is to get the pixels from bitmap file, recalculate the value of each pixel with math algorithm, then restore it in the originate picture. With that, the program can do many interesting effect as if you can figure out an interesting algorithm. In my application, I have done some algorithm such as negative, tint, oil-paint, emboss ,gray-level, sunlight ,etc.

Metro style application is more like WPF. The old program I wrote is using GDI +, so I tried to modify it to a WPF application. It is interesting that you can find it need some effort to port it to different windows platforms. It is because some of the APIs and namespace are different, so it take time to modify the structure of your program. To get a whole picture, I will introduce how to process bitmap image process in GDI+,WPF, winphone7, metro style application.

Bitmap access in different platform

The way GDI+ handle bitmap is straightforward. It uses the Bitmap class to access the bitmap. The most useful method are SetPixel and GetPixel. To do the work, we need firstly declare a Bitmap class as following:

C#
Bitmap original;

After that we declare a pixel ‘Target’ with System.Drawing.Color structure to store the value of R, G, B.

C#
System.Drawing.Color Target;

After that we can use GetPixel to get the value of pixel and store it into target.

C#
Target= original.GetPixel(x, y);

int r = Target.R;
int g = Target.G;
int b = Target.B;

After that, we can process the content of each R, G, B. For example, in the following, we just get the average value of RGB.

C#
int avg = (r + g + b) / 3;

Then we use can use SetPixel to set the new value to the bitmap.

C#
original.SetPixel(x, y, System.Drawing.Color.FromArgb(avg, avg, avg));

After GDI+, let’s talk about WPF. In WPF, the way to handle bitmap is a little different. It uses BitmapImage class to access the bitmap and use CopyPixels to get the pixel of the bitmap picture to a byte array. To use CopyPixels, we need to use a Rect structure to set the range of the bitmap.

C#
BitmapImage iSrc;
var array = new int[iSrc.PixelWidth * iSrc.PixelHeight];
var rect = new Int32Rect(0, 0, iSrc.PixelWidth,iSrc.PixelHeight);
iSrc.CopyPixels(rect, array, iSrc.PixelWidth * 4, 0);

To get the R, G, B value of the pixel, because it is an integer array, we need to do bit shift to get the value of R, G, B. The Blue value of the pixel is:

C#
byte Blue= (byte)((array [index]&  0x000000FF);

The Green of the pixel is:

C#
byte Green= (byte)((array [index]& 0x0000FF00)>>8);

The Red of the pixel is:

C#
byte  Red =(byte)(( array [index]& 0x00FF0000) >> 16);

The Alpha of the pixel is:

C#
byte Alpha=(byte)(( array [index]& 0xFF000000) >> 24);

After that we can process the R, G, B value of the pixel as well. Then combine value to the integer array.

C#
array [index]=(Blue)|(Green<<8)|(Red<<16)|(Alpha<<24);

Then we use the modified pixel array to create a new bitmap to show the result:

C#
BitmapImage.Create(modifiedImage.PixelWidth,
modifiedImage.PixelHeight, 96, 96, PixelFormats.Bgra32, null, array,
pixelsNewsize);

After WPF , let’s talk about WP7. In WP7, we can’t direct modify the pixels of bitmap, we need to use ‘WriteableBitmap’ to access bitmap. The code to create a ‘WriteableBitmap’ is as followings:

C#
BitmapImage bitmap= new BitmapImage();
bitmap.SetSource(value);WriteableBitmap;
modifiedImage = new WriteableBitmap(bitmap);

WriteableBitmap include a byte array ‘Pixel’s to store the whole R,G,B value of Pixels.

C#
byet Blue = (byte)(rawpixel.Pixels[offset]& 0x000000FF); 
byet Green =(byte)((rawpixel.Pixels[offset] & 0x0000FF00) >> 8);
byet Red    =  (byte)((rawpixel.Pixels[offset]& 0x00FF0000) >> 16);
byet Alpha= (byte)((rawpixel.Pixels[offset] & 0xFF000000) >> 24);

After calculating the new value, we use the following way to put the new value back.

C#
rawimagepixel.Pixels[offset] =
  (pixels.Blue) | (pixels.Green << 8) | (pixels.Red << 16) | (pixels.Alpha<<24);

Metro style APP use WinRT. One of the major changes between Silverlight and WinRT are namespace names. Rather than System.Windows.----- , we now use Windows.UI.Xaml.----. There is a document mention of interest: Migrating a Windows Phone 7 app to XAML.

In Metro APP, we use the following way to access bitmap. You can find thought there is also a WriteableBitmap as well, but there is not pixels property. Beause of that, we need to figure out a way to access pixels. I can use ‘Stream’ to do the job. The code is as following:

C#
WriteableBitmap bitmap;

byte[] pixels;
Stream pixelStream;
bitmap = new WriteableBitmap(width, height);
pixels = new byte[4 * bitmap.PixelWidth *
bitmap.PixelHeight];
pixelStream = bitmap.PixelBuffer.AsStream();

The following method sets a pixel to a particular color:

C#
int index = 4*(y * bitmap.PixelWidth + x);

pixels[index + 0] = Pcolor.B;
pixels[index + 1] = Pcolor.G;
pixels[index + 2] = Pcolor.R;
pixels[index + 3] = Pcolor.A;

When you need to update the WriteableBitmap from the pixel array, you need to use seek to set the position of the pixel and use write method to write it:

C#
pixelStream.Seek(0,SeekOrigin.Begin);pixelStream.Write(pixels,0, pixels.Length);

Below is the outlook of my sample Metro application for image processing:

Image 1

Image processing algorithm

All right, now we know how to treat with the pixels in Metro style APP, the next step I would like to introduce is to talk about how to use cool algorithm to modify the pixels. First , let us try to process a negative photo effect . The algorithm is use 255 to minus the R,G,B value of each pixel. The result should between 0 ~255 , the algorithm is as following:

C#
b= (byte)(255 - PC.Blue);
g= (byte)(255 - PC.Green);
r= (byte)(255 - PC.Red);

StreamBuffer[offset+2] = (byte)(r > 255 ? 255 : (r < 0 ? 0 : r));
StreamBuffer[offset+1] =(byte)(g > 255 ? 255 : (g< 0 ? 0 : g));
StreamBuffer[offset+0]=(byte)(b> 255 ? 255 : (b< 0 ? 0 : b));

After processing negative algorithm, the result looks like the following one.

Image 2

The second algorithm I would like to talk about is color filter. In following code, R,G,B is the rational factors to control the R,G,B value of the pixel. We can use the R,G,B as a gain to control the new R,G,B value. The same, the value must located between 255 and 0 .The algorithm is as following:

C#
b= (byte)(PC.Blue * R);
g= (byte)(PC.Green * G);
r= (byte)(PC.Red * B); 

StreamBuffer[offset+2]= (byte)(r > 255 ? 255 : (r < 0 ? 0 : r));
StreamBuffer[offset+1] =(byte)(g > 255 ? 255 : (g< 0 ? 0 : g));
StreamBuffer[offset+0]=(byte)(b> 255 ? 255 : (b< 0 ? 0 : b));

Here we put G as 1, R as 0, G as 0. The output is as followings:

Image 3

The third image algorithm is called emboss, we will do the effect with getting two neighbor pixels and get the R,G,B distance of the two pixels then add an offset to be the new value of R,G,B . The algorithm is as following:

C#
PC1 = GetPixel(i, j, streambuffer, w, h);
PC2 = GetPixel(i+1, j+1, streambuffer,w, h);

r= Math.Abs(PC1.Red - PC2.Red + 128);
g= Math.Abs(PC1.Green - PC2.Green + 128);
b= Math.Abs(PC1.Blue - PC2.Blue + 128);

StreamBuffer[offset+2]= (byte)(r > 255 ? 255 : (r < 0 ? 0 : r));
StreamBuffer[offset+1] =(byte)(g > 255 ? 255 : (g< 0 ? 0 : g));
StreamBuffer[offset+0]=(byte)(b> 255 ? 255 : (b< 0 ? 0 : b));

The output of the photo is as following:

Image 4

The fourth one is sunlight, we define a radius R, if the distance of current point is less then R then use 200 * (1 - MyLength / R) as the new value. The algorithm is as following:

C#
float MyLength = (float)Math.Sqrt(Math.Pow((i - MyCenter.X), 2) + 

Math.Pow((j - MyCenter.Y),2));    

if (MyLength < R)
{
      PC = GetPixel(i, j, streambuffer, w, h);
      float MyPixel = 200 * (1 - MyLength / R);
      int  r = PC.Red + (int)MyPixel;
      PC.Red = (byte)Math.Max(0, Math.Min(r, 255));
      Int g = PC.Green + (int)MyPixel;
      PC.Green = (byte)Math.Max(0, Math.Min(g,255));
      Int b = PC.Blue + (int)MyPixel;
      PC.Blue = (byte)Math.Max(0, Math.Min(b, 255));
      PutPixel(streambuffer, w, h, PC, i, j);
}

The output of the photo is as following:

Image 5

The fifth algorithm I will like to talk is set photo to gray level style, we set the same value to new r,g,b. The algorithm is as following:

C#
r = (byte)((0.311 * r1) + (0.486 * g1)+(0.213 * b1));
StreamBuffer[offset+2]= (byte)(r > 255 ? 255 : (r < 0 ? 0 : r));
StreamBuffer[offset+1] =r;
StreamBuffer[offset+0]=r;

The output of the photo is as following:

Image 6

The sixth is Brightness, it is quite easy. We add a constant value to the new pixel. The algorithm is as following:

C#
r = r_original + bright;
g = g_original+ bright;
b = b_original+ bright;

The output of the photo is as following:

Image 7

The seventh is oil paint, the main concept is to get random points of the bitmap and replace its new position to the random data get in software. The algorithm is as follows:

C#
Random rnd = new Random();
int iModel= 10;
int i =w - iModel;

while (i> 1)
{
    int j= h - iModel;
  
    while(j > 1)
    {
       int iPos = rnd.Next(100000) % iModel;
         
       PC = GetPixel((int)(i + iPos), (int)(j + iPos), modifiedstreambuffer, w,
         h);
                 
       PutPixel(bitmapstreambuffer, w, h, PC, i, j);
       j= j - 1;
     }
     i = i - 1;
}

The output is as follows:

Image 8

All the effect is quite interesting, right? Of course, you can create more amazing image effect algorithm created by yourself. By the way, we can also add camera function in the image processing program, so after take a picture, we can put special effect for that immediately. Part of the code is as following.

C#
var ui = new CameraCaptureUI();
ui.PhotoSettings.CroppedAspectRatio= new Size(4, 3);
var file = await
ui.CaptureFileAsync(CameraCaptureUIMode.Photo);
             
stream = await file.OpenAsync(FileAccessMode.Read);
var bitmap = new BitmapImage();
             
bitmap.SetSource(stream);
             
Image1.Source = bitmap;

At the end of the article, let us talk about some performance issues of bitmap processing. We use negative algorithm as an example to discuss it. To improve the processing speed, we can:

  • Change two ‘for’ loop into one for loop, that’s means process one strip instead of one pixel.
  • Read all pixels to a memory buffer but read one pixel directly.

In the sample program, in the beginning, I process pixels with two ‘for’ loops , it take s long time to process the value of pixels. The performance is not good.

C#
for (i = 0;i < modifiedImage.PixelWidth; i++)
{
    for(int j = 0; j < modifiedImage.PixelHeight; j++)
    {
        PC = GetPixel(i, j, streambuffer,w,h);
        PC.Blue = (byte)(255 - PC.Blue);
                 
        PC.Green = (byte)(255 - PC.Green);
        PC.Red = (byte)(255 - PC.Red);
                 
        PutPixel(streambuffer,w,h, PC, i, j);
    }
}

After I shrink two for loop to one as the following:

C#
for (i = 0;i < streambuffer.Length-4; i=i+4)
{
    streambuffer[i + 3] = (byte)( 0xff - streambuffer[i + 3]);
    streambuffer[i + 2] = (byte)( 0xff - streambuffer[i + 2]);
    streambuffer[i + 1] = (byte)( 0xff - streambuffer[i + 1]);
    streambuffer[i + 0] = (byte)( 0xff - streambuffer[i + 0]);
}

It did improve performance a lot.

Hope the article and sample program can help you know more about image effect in Metro style application.

License

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


Written By
Software Developer (Senior)
Taiwan Taiwan
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionMetroImage load failed Pin
Member 1144726117-Feb-15 19:18
Member 1144726117-Feb-15 19:18 
QuestionI think you mean GDI+ not GDK+ Pin
raildude5-Dec-14 9:33
professionalraildude5-Dec-14 9:33 
AnswerRe: I think you mean GDI+ not GDK+ Pin
David_Ke30-Dec-14 17:35
David_Ke30-Dec-14 17:35 
GeneralMy vote of 5 Pin
Prasad Khandekar26-Nov-14 5:19
professionalPrasad Khandekar26-Nov-14 5:19 
GeneralVery nice! Pin
Prasad Khandekar28-May-13 23:00
professionalPrasad Khandekar28-May-13 23:00 
SuggestionNice Pin
SahebSoft20-May-13 7:01
SahebSoft20-May-13 7:01 
QuestionCool! Pin
Adhe Nurcahya15-May-13 16:45
Adhe Nurcahya15-May-13 16:45 
QuestionGreat article - question Pin
Meg D8-Mar-13 20:17
Meg D8-Mar-13 20:17 
AnswerRe: Great article - question Pin
Farhan Ghumra25-Mar-13 22:21
professionalFarhan Ghumra25-Mar-13 22:21 
QuestionMetro Style Lightweight Image Processing Pin
kuldeep3456721-Jan-13 20:15
kuldeep3456721-Jan-13 20:15 
AnswerRe: Metro Style Lightweight Image Processing Pin
Farhan Ghumra25-Mar-13 21:40
professionalFarhan Ghumra25-Mar-13 21:40 
Questionthanks for sharing Pin
Hellowlf23-Jul-12 20:59
Hellowlf23-Jul-12 20:59 
QuestionGetting errror Pin
Farhan Ghumra6-Jul-12 1:24
professionalFarhan Ghumra6-Jul-12 1:24 
QuestionUrgent - how to modify the function GrayScale() to use in windows 8 metro application Pin
RahnaAlfia15-Apr-12 21:15
RahnaAlfia15-Apr-12 21:15 
Hi,
I am using Windows 8 beta and visual studio 11 express beta
It didn't support
WriteableBitmap(bitmap);
It only support WriteableBitmap(Width,Height)

And all your functions parameters are
WriteableBitmap

I am using the code like

FileOpenPicker openPicker = new FileOpenPicker();
openPicker.SuggestedStartLocation = PickerLocationId.PicturesLibrary;
openPicker.FileTypeFilter.Add(".jpg");
openPicker.FileTypeFilter.Add(".jpeg");
openPicker.FileTypeFilter.Add(".png");

// Open a stream for the selected file
StorageFile file = await openPicker.PickSingleFileAsync();
if (null != file)
{
IRandomAccessStream fileStream = await file.OpenAsync(Windows.Storage.FileAccessMode.Read);
// Application now has read/write access to the picked file
BitmapImage bitmapImage = new BitmapImage();
bitmapImage.SetSource(fileStream);
Img1.Source = bitmapImage;
fileStream.Dispose();
}

How should I modify to use the function

private WriteableBitmap GrayScale(WriteableBitmap modifiedImage)
{}
QuestionThanks for sharing Pin
Patrick Kalkman6-Apr-12 21:49
Patrick Kalkman6-Apr-12 21:49 
Generalmy vote of 5 Pin
Uday P.Singh6-Apr-12 2:02
Uday P.Singh6-Apr-12 2:02 
BugProblem Plzz Help Pin
Rahulpuroht6-Apr-12 0:20
Rahulpuroht6-Apr-12 0:20 
QuestionGreat comparative article, but question ? Pin
samba-lee22-Mar-12 12:18
samba-lee22-Mar-12 12:18 
AnswerRe: Great comparative article, but question ? Pin
David_Ke25-Mar-12 19:53
David_Ke25-Mar-12 19:53 
QuestionVery good article. Pin
Anitesh Kumar21-Mar-12 23:49
Anitesh Kumar21-Mar-12 23:49 
AnswerRe: Very good article. Pin
David_Ke25-Mar-12 19:24
David_Ke25-Mar-12 19:24 
QuestionGreat article but... (a bit off topic) Pin
Nick__J18-Mar-12 12:06
Nick__J18-Mar-12 12:06 
AnswerRe: Great article but... (a bit off topic) Pin
David_Ke25-Mar-12 19:35
David_Ke25-Mar-12 19:35 
QuestionInteresting Pin
yvdh11-Mar-12 23:33
yvdh11-Mar-12 23:33 
AnswerRe: Interesting Pin
David_Ke14-Mar-12 5:48
David_Ke14-Mar-12 5:48 

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.