For some reason Microsoft made it pretty hard to work with anything but 24bit images. This is even though they provide you with several pixel formats, but no way of setting and getting the values of a pixel. If you use the SetPixel(x,y) or GetPixel(x,y) methods, your application will fail. There are plenty of articles and blogs on the Internet on how to do direct access on 1bit and 24bit, but I wasn't able to find anything on 8bit.
处于某些原因,微软让工作在非24位图像环境下变得非常困难。这甚至透过他们给你提供几种像素格式,却没法获得和设置它的值也可以看出。如果你使用SetPixel(x,y)或GetPixel(x,y) 方式,你就会请求失败。网上有大堆的文章和博客讲直接使用1位和24位图,但是我却没找到任何有关8位的信息。
This article will cover some of the basics on how to access 8 bit greyscale or indexed images, by accessing the bitmapdata directly in memory. This also has the benefit of being much faster than the Set/GetPixel methods provided by the .NET Framework.
本篇文章将会讲解一些基本的通过直接获得内存中的位图数据来使用8位灰度或indexed images(索引的图像)。它同时还有比.net Framework提供的Set/GetPixel 方法更快的特点。
Before we can access the memory directly, we must lock its place in memory. We can do this by calling the Bitmap.LockBits() method:
在我没直接或的内存数据之前,我们必须锁定它在内存中的地址。可以用Bitmap.LockBits() 方法达到:
BitmapData bmd = myBitmap.LockBits(new Rectangle(0, 0, myBitmap.Width, myBitmap.Height),
ImageLockMode.ReadWrite, myBitmap.PixelFormat);
Likewise when we are done using the BitmapData, remember to unlock the data:
同样,当你在用BitmapData时,别忘了解除数据锁定:
myBitmap.UnlockBits(bmd);
Now we need a method that can access the BitmapData. Lets make our own SetPixel and GetPixel method. Here we assume that we are dealing with 8bit pixels. We also add the 'unsafe' keyword since direct memory access isn't thread safe. I won't cover the Stride and Scan0 values. Bob Powell has a nice article on this.
现在我们需要一个能存取BitmapData的方法。让我们创造自己的SetPixel and GetPixel 方法。假定我们正在处理8位像素。我们也添加了“不安全”的关键词因为直接内存获取并不安全。我不会讲述Stride and Scan0 值。Bob Powell 有一篇关于它的文章。
public unsafe void SetPixel(int x, int y, byte c)
{
byte* p = (byte *)bmd.Scan0.ToPointer();
int offset=y*bmd.Stride+(x);
p[offset] = c;
}
public unsafe Byte GetPixel(int x, int y)
{
byte* p = (byte *)bmd.Scan0.ToPointer();
int offset=y*bmd.Stride+x;
return p[offset];
}
It is worth noting that GetPixel only returns a byte and not a color. The byte represents a number between 0 and 255. Each of the values is actually an index to a color palette. The palette could specify that for instance index 0 is black, index 1 is red, index 3 is blue etc. If you want a greyscale image, we can override the color palette. Let's set index 0 to black, index 255 to white, and linearly distribute the grayscale in between.
GetPixel 只返回了一个字节而并不是颜色,这对我们没什么用。这个字节代表了一个从0到255的数。每一个值代表了一种颜色,0代表黑色,1代表红色,3代表蓝色等等。如果你想要一个灰度图像,我们可以不考虑颜色。我们设置0为黑色,255为白色,灰色线性的分布在中间。
public static void SetGrayscalePalette(Bitmap b)
{
ColorPalette pal = b.Palette;
for(int i = 0; i < 256; i++)
pal.Entries[i] = Color.FromArgb( 255, i, i, i );
b.Palette = pal;
}
You can easily override this palette to specify other than grayscale images.
你也可以不考虑这个灰阶图像而把它指定为别的颜色。
We can likewise create a function that can convert an index to a System.Drawing.Color. If you are working with a grayscale image, there is probably no need for this.
相同的我们可以创造一个函数区转换颜色代号到System.Drawing.Color。如果你正在处理灰阶图像就没这个必要了。
public System.Drawing.Color GetColorFromIndex(byte c)
{
return = myBitmap.Palette.Entries[c];
}
Now let's put it all together into an easy-to-use 8bit image access class. Remember to allow unsafe code blocks before compiling.
先让我们把它们和在一起成为一个使用8位图像的程序片段。记住在计算之前要解除不安全代码锁定。
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
namespace ImageProc
{
/// <summary>
/// Class used for direct memory access to 8bit grayscale images
/// </summary>
public class Image8Bit : IDisposable
{
private BitmapData bmd;
private Bitmap b;
/// <summary>
/// Locks an 8bit image in memory for fast get/set pixel functions.
/// Remember to Dispose object to release memory.
/// </summary>
/// Bitmap reference
public Image8Bit (Bitmap bitmap)
{
if(bitmap.PixelFormat!=System.Drawing.Imaging.PixelFormat.Format8bppIndexed)
throw(new System.Exception("Invalid PixelFormat. 8 bit indexed required"));
b = bitmap; //Store a private reference to the bitmap
bmd = b.LockBits(new Rectangle(0, 0, b.Width, b.Height),
ImageLockMode.ReadWrite, b.PixelFormat);
}
/// <summary>
/// Releases memory
/// </summary>
public void Dispose()
{
b.UnlockBits(bmd);
}
/// <summary>
/// Gets color of an 8bit-pixel
/// </summary>
/// <param name="x">Row</param>
/// <param name="y">Column</param>
/// <returns>Color of pixel</returns>
public unsafe System.Drawing.Color GetPixel(int x, int y)
{
byte* p = (byte *)bmd.Scan0.ToPointer();
//always assumes 8 bit per pixels
int offset=y*bmd.Stride+x;
return GetColorFromIndex(p[offset]);
}
/// <summary>
/// Sets color of an 8bit-pixel
/// </summary>
/// <param name="x">Row</param>
/// <param name="y">Column</param>
/// <param name="c">Color index</param>
public unsafe void SetPixel(int x, int y, byte c)
{
byte* p = (byte *)bmd.Scan0.ToPointer();
//always assumes 8 bit per pixels
int offset=y*bmd.Stride+(x);
p[offset] = c;
}
/// <summary>
/// Sets the palette for the referenced image to Grayscale
/// </summary>
public void MakeGrayscale()
{
SetGrayscalePalette(this.b);
}
/// <summary>
/// Sets the palette of an image to grayscales (0=black, 255=white)
/// </summary>
/// <param name="b">Bitmap to set palette on</param>
public static void SetGrayscalePalette(Bitmap b)
{
ColorPalette pal = b.Palette;
for(int i = 0; i < 256; i++)
pal.Entries[i] = Color.FromArgb( 255, i, i, i );
b.Palette = pal;
}
private System.Drawing.Color GetColorFromIndex(byte c)
{
return = b.Palette.Entries[c];
}
}
}

