Video Manipulating Bits

Attach:EasyImagesManipulatingBits.png << Back to the EasyImage Toolkit page

Tutorial - Manipulating Bits in a Bitmap

What you will learn

This tutorial is a companion to the How-To: Understanding Bitmaps. Read that first! This tutorial shows:

  • how you can manipulate bits in a bitmap.
  • note that this is not really a tutorial on EasyImages, but knowing how you can manipulate successive frames at a low level is really useful if you need a novel effect.

In the above picture,

  • the image is the result of saturating some pixel colors by boosting values above 127 to 256. Alternatively, one can washout the image (not shown) by restricting pixel colors so they never go above 127. Color values range from 0 - 255.

Download

While we recommend you create this program from scratch, you can also download the source and executables.

Source and Executables of this and other examples: EasyImagesExamples.zip
  • Unzip all files into a single folder.
  • Try the executable; a shortcut is in the top-level directory, or look in the debug directory
  • You will need a web camera attached to your computer.

Step 1. Preconditions

Including EasyImages in your new Visual Studio 2005 C# project. This was described in Minimal Camera Example. Make sure to include the using EasyImages; line in your project.

Step 2. Creating GUI controls

Add the following GUI controls to your form window. Thei locations and purpose should be obvious if you check the image at the top of this page.

  • 2 PictureBoxes
    • Name = pbOriginal, pbAltered
    • Size = 320,240 (the Width and Height of the frame we will put in it)
  • 2 Labels
    • Name = lblOriginal, lblAlteredImage
    • Text = Original Image, Altered Image
  • 1 Groupbox
    • Text = Effect
  • '''2 RadioButtons
    • Name = rbSaturate, rbWashout
    • Text = Saturate, Wash Out

Step 3. Writing the program

The complete program is listed below.

using System;
using System.Drawing;
using System.Windows.Forms;
using System.Drawing.Imaging; //Needed
using EasyImages;
namespace VideoManipulatingBits
{
  public partial class Form1 : Form
  {
    EasyImages.CameraClient camera;
    private delegate void SetPictureBoxImage(PictureBox pbox, Bitmap image);

    public Form1()
    {
      InitializeComponent();
    }

    //As the GUI is loaded, create a new camera object and set its initial properties
    private void Form1_Load(object sender, EventArgs e)
    {
      //Create a new camera object and set its initial properties
      camera = new EasyImages.CameraClient("DefaultCamera");
      camera.FramesPerSecond = 10;
      camera.ReceivedFrame += new EasyImages.CameraClient.ReceivedFrameEventHandler(camera_ReceivedFrame);
      camera.Start();
    }

    // Display the Image plus the altered image
    void camera_ReceivedFrame(object sender, EasyImages.CameraEventArgs e)
    {
      //Display the original video frame
      DisplayImageInPictureBox (this.pbOriginal, e.Bitmap);
      //Make a copy of the bitmap, manipulate it, and display it
      Bitmap bm = e.Bitmap.Clone() as Bitmap;
      ManipulateBits(bm);
      DisplayImageInPictureBox (this.pbAltered, bm);
    }

    //Display the image in the picture box in the correct thread
    private void DisplayImageInPictureBox(PictureBox pbox, Image image)
    {
      if (pbox.InvokeRequired) // We are in the wrong thread. Call ourselves in the correct thread
      {
        SetPictureBoxImage theDelegate = new SetPictureBoxImage(DisplayImageInPictureBox);
        BeginInvoke(theDelegate, new object[] { pbox, image });
      }
      else // we are in the correct thread, so assign the image
      {
        pbox.Image = image;
      }
    }

  // Note: needs to be compiled in UNSAFE mode!
  // Select the Build tab in the Project Properties and check the 'Allow Unsafe Code' checkbox
  private void ManipulateBits (Bitmap bm)
  {
    // We must first lock the entire image so that we can manipulate it.
    // The BitmapData class allows us to manipulate it.
    System.Drawing.Imaging.BitmapData data =
      bm.LockBits(new Rectangle(0, 0, bm.Width, bm.Height),
                  ImageLockMode.ReadWrite,
                  PixelFormat.Format24bppRgb);
    unsafe
    {
      // Create a pointer to the first Byte of the structure
      byte* imgPtr = (byte*)(data.Scan0);
      // Now we will walk through the bitmap by row and column
      for (int row = 0; row < data.Height; row++)
      {
        for (int col = 0; col < data.Width; col++)
        {
          // The pixel is 3 bytes long one for each the blue, green, and red values:
          byte blue = (byte)(*imgPtr);
          ManipulateColor(imgPtr);
          // advance one step to the green value
          imgPtr++;
          byte green = (byte)(*imgPtr);
          ManipulateColor(imgPtr);
          imgPtr++;
          // advance one step to the red value
          byte red = (byte)(*imgPtr);
          ManipulateColor(imgPtr);
          imgPtr++;
        }
        // Go to the next row.  Stride gets the offset for the next row,
        // but we have already moved Data.Width*3 through the current row,
        // so we subtract that.
        imgPtr += data.Stride - data.Width * 3;
      }
    }
    // Our work is done, so we can unlock the bitmap.
    bm.UnlockBits(data);
  }

  //Depending on which radio button was selected,
  // either saturate the color held in the byte or wash it out
  //Note that color values range from 0 (none) to 255 (fully saturated)
  unsafe private void ManipulateColor (byte *b)
  {
    if (rbSaturate.Checked)
    {
      // Saturate the color if its above 127, otherwise nothing
      if (*b > 127)
        *b = 255;
      else
        *b = 0;
    }
    else if (rbWashOut.Checked)
    {
      // The maximum any color can have is reduced to 127
      if (*b > 127)
        *b = 127;
    }
   }
  }
}

Explanation

Much of this program is similar to what was seen in the Minimal Camera, so we only describe what is different. Full details on how the critical code in ManipulateBits works is found in the How-To: Understanding Bitmaps and should be read.

  • ManipulateBits gets a bitmap, and walks through it pixel by pixel and byte by byte. After it gets a byte (representing a particular R,G or B value), it then passes it on to ManipulateColor for further processing.
  • ManipulateColor checks the value of the color and, depending on the state of the radiobutton, changes the color to a different value. For Saturate, it takes any value above 127 and boosts it to a maximum value of 255. For Wash Out, it restricts colors to a maximum value of 127.