Help viewing seperate bitmap channels

FireScythe

New member
Joined
Jun 18, 2006
Messages
4
Hi, Im currently making a program that opens a bitmap for viewing from a non-standard format. I have succeeded in getting the bitmap data and have opened it into a Bitmap object. Now I want to be able to select one (or a mix of ARGB) of the colour channels to display.

I have achieved this by cycling through all of the pixels using for statements and changing the pixels ARGB colour values one by one. But as was expected this takes alot of time with larger images.

Is there a quicker (much quicker) way of doing this?
 
There is, but it isnt nearly as simple to write as it would be using GetPixel/SetPixel. Youll need to understand image manipulation (Im sure you do) and binary math.

Simply put, you would create an array, use the Marshal class (not sure which namespace) to copy the raw pixel data to the array, modify the raw pixel data, and finally copy it back to the bitmap.


First create an array (Int32s and UInt32s are ideal, especially with 32-bit ARGB images) with the appropriate size to hold pixel data. For a 32-bit bitmap, the size of the array would simply be the width of the image multiplied by the height of the image.

Next you would lock the bitmap. The object returned by the Lock function contains a pointer to the raw pixel data (named Scan0). Call Marshal.Copy to copy the data from Scan0 to your array. For the length argument, pass the length of the array.

Now you can modify the raw pixel data. For example, to view only blue data (exclude red, green, and alpha), use could use code akin to the following:
C#:
// storing raw image data in img[]
// IM NOT SURE ABOUT THE BYTE ORDER
int fullAlpha = 0xFF << 24; 
int blueMask = 0xFF; 
for(int i = 0; i < img.Length; i++) {
    img[i] = img[i] & blueMake // Extract blue data
        | fillAlpha; // and make it fully opaque.
}

Lastly, you would use Marshal.Copy to copy the image data back to the bitmap and unlock the bitmap.



Some people might tell you to forget the whole Array/Marshal.Copy thing and use pointers, but that requires unsafe code (more permissions), can only be done in C#, and in my experience, actually seems to be slower than using the Marshal class.

If you run into any problems coding, Ill be glad to give you a hand
 
Working channel viewing

Thankyou very much for your help ;) . After doing a bit of research (and trial and error) I managed to get it working, and its much faster than my previous method.

Heres the code (with minor alterations) that im now using, incase anybody else is interested or if improvements can be made:

(This is C# code.)
Code:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Drawing.Imaging;
Code:
Bitmap BitmImage;
private void ChangeChannel(bool A,bool R,bool G,bool B)
{
    //The bitmap is re-loaded on each run to replace channels that have 
    //already been removed
    BitmImage = new Bitmap("c:\\Bitmap.bmp") //Your bitmap object/file goes here.

    //Lock the bitmap into unmanaged memory.
    BitmapData BD = BitmImage.LockBits(new Rectangle(new Point(0, 0),   
        BitmImage.Size), ImageLockMode.ReadWrite,
        PixelFormat.Format32bppArgb);
       
    //Set up the array that will accomodate the raw bitmap data.
    Int32[] RawImgData = new Int32[BitmImage.Width * BitmImage.Height];

    //Copy the locked bitmap data into the previously made array from the Scan0 pointer
    //in the BitmapData object.
    Marshal.Copy(BD.Scan0, RawImgData, 0, RawImgData.Length);

    Int32 AlphaMask = 0;
    Int32 RedMask = 0;
    Int32 GreenMask = 0;
    Int32 BlueMask = 0;

    //If the alpha boolean (A) it set to true, ignore the Red,Green and Blue channels.
    //The values are byte-shifted to match the 32 bit ARGB Format.
    AlphaMask = 0xFF << 24;
    if (!A && R) { RedMask = 0xFF << 16; } else { RedMask = 0x00 << 16; }
    if (!A && G) { GreenMask = 0xFF << 8; } else { GreenMask = 0x00 << 8; }
    if (!A && B) { BlueMask = 0xFF << 0; } else { BlueMask = 0x00 << 0; }

    //Cycle through the raw data, altering the image to remove unwanted channels.
    for (int i = 0; i < RawImgData.Length; i++)
    {
        Int32 tmp = 0;
        if (A)
        {
            //Make "tmp" equal the Alpha value for this pixel.
            tmp = RawImgData[i] & AlphaMask;

            //Replace the alpha value with FF to make the image fully opaque.
            RawImgData[i] = 0xFF << 24; 

            //Byte-shift the alpha value and replace the colour channels to make 
            //a white alpha representation.
            RawImgData[i] += tmp >> 8;  
            RawImgData[i] += tmp >> 16;
            RawImgData[i] += tmp >> 24;
        }
        else
        {
        // Perform a logical AND operation on the pixel data to remove unwanted data.
        tmp += RawImgData[i] & RedMask;
        tmp += RawImgData[i] & GreenMask;
        tmp += RawImgData[i] & BlueMask;
        RawImgData[i] = tmp | AlphaMask;
        }
    }
        
    //Copy the edited bitmap data back to the unmanaged memory location, Scan0.
    Marshal.Copy(RawImgData, 0, BD.Scan0, RawImgData.Length);
    BitmImage.UnlockBits(BD); //Unlock the bitmap object and use the edited bitmap.
    ViewBox.Size = BitmImage.Size;
    ViewBox.Image = BitmImage;
}
 
Back
Top