All notes
ImageProcessing

Basics

GDI

The Microsoft Windows graphics device interface (GDI) enables applications to use graphics and formatted text on both the video display and the printer. Windows-based applications do not access the graphics hardware directly. Instead, GDI interacts with device drivers on behalf of applications. MSDN: Windows GDI. Windows GDI+ is a class-based API for C/C++ programmers.

GDI+ functions and classes are not supported for use within a Windows service. Attempting to use these functions and classes from a Windows service may produce unexpected problems, such as diminished service performance and run-time exceptions or errors.

The Microsoft Windows graphics device interface (GDI) enables applications to use graphics and formatted text on both the video display and the printer. Windows-based applications do not access the graphics hardware directly. Instead, GDI interacts with device drivers on behalf of applications. MSDN: Windows GDI. Windows GDI+ is a class-based API for C/C++ programmers.

GDI+ functions and classes are not supported for use within a Windows service. Attempting to use these functions and classes from a Windows service may produce unexpected problems, such as diminished service performance and run-time exceptions or errors. TODO wcfNote: then what can we use for service?

GDI+ provides the Bitmap class for working with raster images and the Metafile class for working with vector images. The Bitmap and the Metafile classes both inherit from the Image class.

References

Bitmap


GetPixel(Int32, Int32);
SetPixel(Int32, Int32, Color);

LockBits(Rectangle, ImageLockMode, PixelFormat);
LockBits(Rectangle, ImageLockMode, PixelFormat, BitmapData);
UnlockBits(BitmapData);

Save(String);
Clone();

Images are drawn to the screen or to memory by using the DrawImage method of the Graphics object.

LockBits()

This page provides a test result that lockBits() is seven times faster than simply set/getPixel().

This page says lockBits() uses the whole image, thus there is concept of strides.

Cast IntPtr to byte*:


var bitmap = new Bitmap(100, 100);

var data = bitmap.LockBits(new Rectangle(0, 0, 10, 10),
                           ImageLockMode.ReadWrite,
                           bitmap.PixelFormat);

var pt = (byte*)data.Scan0;
var bpp = data.Stride / bitmap.Width; // Byte per pixel.

for (var y = 0; y < data.Height; y++)
{
    // This is why real scan-width is important to have!
    var row = pt + (y * data.Stride);

    for (var x = 0; x < data.Width; x++)
    {
        var pixel = row + x * bpp;
        for (var byt = 0; byt < bpp; byt++)
        {
            var pixelComponent = pixel[byt];
        }
    }
}

bitmap.UnlockBits(data);

Mashal.Copy()


private void MakeMoreBlue(Bitmap bmp)
{
    // Specify a pixel format.
    PixelFormat pxf = PixelFormat.Format24bppRgb;

    // Lock the bitmap's bits.
    Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
    BitmapData bmpData =
    bmp.LockBits(rect, ImageLockMode.ReadWrite,
                 pxf);

    // Get the address of the first line.
    IntPtr ptr = bmpData.Scan0;

    // Declare an array to hold the bytes of the bitmap.
    // int numBytes = bmp.Width * bmp.Height * 3;
    int numBytes = bmpData.Stride * bmp.Height;
    byte[] rgbValues = new byte[numBytes];

    // Copy the RGB values into the array.
    Marshal.Copy(ptr, rgbValues, 0, numBytes);

    // Manipulate the bitmap, such as changing the
    // blue value for every other pixel in the the bitmap.
    for(int counter = 0; counter < rgbValues.Length; counter+=6)
        rgbValues[counter] = 255;

    // Copy the RGB values back to the bitmap
    Marshal.Copy(rgbValues, 0, ptr, numBytes);

    // Unlock the bits.
    bmp.UnlockBits(bmpData);
}

StackOverflow: image processing with lockbits provides an exmaple for gray bitmap:


public static Bitmap BitmapFromArray1D(byte[] bytes, int width, int height)
{
    Bitmap grayBmp = new Bitmap(width, height, PixelFormat.Format8bppIndexed);
    Rectangle grayRect = new Rectangle(0, 0, grayBmp.Width, grayBmp.Height);
    BitmapData grayData = grayBmp.LockBits(grayRect, ImageLockMode.ReadWrite, grayBmp.PixelFormat);
    IntPtr grayPtr = grayData.Scan0;

    int grayBytes = grayData.Stride * grayBmp.Height;
    ColorPalette pal = grayBmp.Palette;

    for (int g = 0; g < 256; g++){
        pal.Entries[g] = Color.FromArgb(g, g, g);
    }

    grayBmp.Palette = pal;

    System.Runtime.InteropServices.Marshal.Copy(bytes, 0, grayPtr, grayBytes);

    grayBmp.UnlockBits(grayData);
    return grayBmp;
}

Wcf's examples


Rectangle rect = new Rectangle(0, 0, input.Width, input.Height);

rect = new Rectangle(0, 0, input.Width, 1);
// bmpData: Width = 2083, Height = 1, Stride = 6252
// rect = new Rectangle(0, 0, 1, input.Height);
// bmpData: Width = 1, Height = 2560, Stride = 6252

var bmpData = input.LockBits(rect, ImageLockMode.ReadOnly, input.PixelFormat);
Console.WriteLine(bmpData.ToString());

Examples

Paint Bitmap


Bitmap bitmap = new Bitmap("Grapes.jpg");
// PaintEventArgs e, is a parameter of the Paint event handler.
e.Graphics.DrawImage(bitmap, 60, 10);

Metafile metafile = new Metafile("SampleMetafile.emf");
e.Graphics.DrawImage(metafile, 60, 10);

// Affine / Skew an image.
Point[] destinationPoints = {
    new Point(200, 20),   // destination for upper-left point of original
    new Point(110, 100),  // destination for upper-right point of original
    new Point(250, 30)};  // destination for lower-left point of original
Image image = new Bitmap("Stripes.bmp");
// Draw the image unaltered with its upper-left corner at (0, 0).
e.Graphics.DrawImage(image, 0, 0);
// Draw the image mapped to the parallelogram.
e.Graphics.DrawImage(image, destinationPoints);

Avoid automatic scaling

Avoid automatic scaling. MSDN.


Image image = new Bitmap("Texture.jpg");

// If the resolution used by GDI+ (usually 96 dots per inch) is different from the resolution stored in the Image object, then the DrawImage method will scale the image.
e.Graphics.DrawImage(image, 10, 10); // It may scale the image.
// Explicitly use original image's dimension to suppress scaling.
e.Graphics.DrawImage(image, 120, 10, image.Width, image.Height);