Bitmap对象封装了GDI+中的一个位图,此位图由图形图像及其属性的像素数据组成.因此Bitmap是用于处理由像素数据定义的图像的对象.该类的主要方法和属性如下:
1.GetPixel方法和SetPixel方法:获取和设置一个图像的指定像素的颜色.
2.PixelFormat属性:返回图像的像素格式.
3.Palette属性:获取和设置图像所使用的颜色调色板.
4.Height Width属性:返回图像的高度和宽度.
5.LockBits方法和UnlockBits方法:分别锁定和解锁系统内存中的位图像素.在基于像素点的图像处理方法中使用LockBits和UnlockBits是一个很好的方式,这两种方法可以使我们指定像素的范围来控制位图的任意一部分,从而消除了通过循环对位图的像素逐个进行处理,每调用LockBits之后都应该调用一次UnlockBits.
二.BitmapData类
BitmapData对象指定了位图的属性
1.Height属性:被锁定位图的高度.
2.Width属性:被锁定位图的高度.
3.PixelFormat属性:数据的实际像素格式.
4.Scan0属性:被锁定数组的首字节地址,如果整个图像被锁定,则是图像的第一个字节地址.
5.Stride属性:步幅,也称为扫描宽度.
如上图所示,数组的长度并不一定等于图像像素数组的长度,还有一部分未用区域,这涉及到位图的数据结构,系统要保证每行的字节数必须为4的倍数.
三.Graphics类
Graphics对象是GDI+的关键所在,许多对象都是由Graphics类表示的,该类定义了绘制和填充图形对象的方法和属性,一个应用程序只要需要进行绘制或着色,它就必须使用Graphics对象.
四.Image类
这个类提供了位图和元文件操作的函数.Image类被声明为abstract,也就是说Image类不能实例化对象,而只能做为一个基类.
1.FromFile方法:它根据输入的文件名产生一个Image对象,它有两种函数形式:
public static Image FromFile(string
filename);
public static Image FromFile(string
filename, bool useEmbeddedColorManagement);
2.FromHBitmap方法:它从一个windows句柄处创建一个bitmap对象,它也包括两种函数形式:
public static bitmap fromhbitmap(intptr
hbitmap);
public static bitmap fromhbitmap(intptr
hbitmap, intptr hpalette);
3. FromStream方法:从一个数据流中创建一个image对象,它包含三种函数形式:
public static image fromstream(stream
stream);
public static image fromstream(stream
stream, bool useembeddedcolormanagement);
fromstream(stream stream, bool
useembeddedcolormanagement, bool
validateimagedata);
有了上面的了解,我们便可以开始利用C#做图像处理,下面介绍几种方法:
一.打开、保存、显示图像
private Bitmap srcBitmap
= null;
private Bitmap showBitmap
= null;
//打开文件
private void menuFileOpen_Click(object sender, EventArgs e)
{
OpenFileDialog openFileDialog
= new OpenFileDialog();
openFileDialog.Filter
= @"Bitmap文件(*.bmp)|*.bmp|Jpeg文件(*.jpg)|*.jpg|所有合适文件(*.bmp,*.jpg)|*.bmp;*.jpg";
openFileDialog.FilterIndex
= 3;
openFileDialog.RestoreDirectory
= true;
if (DialogResult.OK
== openFileDialog.ShowDialog())
{
srcBitmap
= (Bitmap)Bitmap.FromFile(openFileDialog.FileName, false);
showBitmap
= srcBitmap;
this.AutoScroll
= true;
this.AutoScrollMinSize
= new Size((int)(showBitmap.Width), (int)(showBitmap.Height));
this.Invalidate();
}
}
//保存图像文件
private void menuFileSave_Click(object sender, EventArgs e)
{
if (showBitmap
!= null)
{
SaveFileDialog saveFileDialog
= new SaveFileDialog();
saveFileDialog.Filter
= @"Bitmap文件(*.bmp)|*.bmp|Jpeg文件(*.jpg)|*.jpg|所有合适文件(*.bmp,*.jpg)|*.bmp;*.jpg";
saveFileDialog.FilterIndex
= 3;
saveFileDialog.RestoreDirectory
= true;
if (DialogResult.OK
== saveFileDialog.ShowDialog())
{
ImageFormat format
= ImageFormat.Jpeg;
switch (Path.GetExtension(saveFileDialog.FileName).ToLower())
{
case ".jpg":
format
= ImageFormat.Jpeg;
break;
case ".bmp":
format
= ImageFormat.Bmp;
break;
default:
MessageBox.Show(this, "Unsupported
image format was specified", "Error",
MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
try
{
showBitmap.Save(saveFileDialog.FileName,format
);
}
catch (Exception)
{
MessageBox.Show(this, "Failed
writing image file", "Error",
MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
}
}
//窗口重绘,在窗体上显示图像,重载Paint
private void frmMain_Paint(object sender,
System.Windows.Forms.PaintEventArgs e)
{
if (showBitmap
!= null)
{
Graphics g
= e.Graphics;
g.DrawImage(showBitmap, new Rectangle(this.AutoScrollPosition.X, this.AutoScrollPosition.Y
,
(int)(showBitmap.Width),
(int)(showBitmap.Height)));
}
}
//灰度化
private void menu2Gray_Click(object sender, EventArgs e)
{
if (showBitmap
== null) return;
showBitmap
= RGB2Gray(showBitmap);//下面都以RGB2Gray为例
this.Invalidate();
}
二.提取像素法
这种方法简单易懂,但相当耗时,完全不可取.
publicstatic Bitmap RGB2Gray(Bitmap srcBitmap)
{
Color srcColor;
int wide
= srcBitmap.Width;
int height
= srcBitmap.Height;
for (int y
= 0; y < height; y++)
for (int x
= 0; x < wide; x++)
{
//获取像素的RGB颜色值
srcColor
= srcBitmap.GetPixel(x, y);
byte temp
= (byte)(srcColor.R * .299 +
srcColor.G * .587 + srcColor.B * .114);
//设置像素的RGB颜色值
srcBitmap.SetPixel(x,
y, Color.FromArgb(temp, temp, temp));
}
return srcBitmap
;
}//#
三.内存法
这是比较常用的方法
publicstatic Bitmap RGB2Gray(Bitmap srcBitmap)
{
int wide
= srcBitmap.Width;
int height
= srcBitmap.Height;
Rectangle rect
= new Rectangle(0, 0, wide, height);
//将Bitmap锁定到系统内存中,获得BitmapData
BitmapData srcBmData
= srcBitmap.LockBits(rect,
ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
//创建Bitmap
Bitmap dstBitmap
= CreateGrayscaleImage(wide, height);//这个函数在后面有定义
BitmapData dstBmData
= dstBitmap.LockBits(rect,
ImageLockMode.ReadWrite, PixelFormat.Format8bppIndexed);
//位图中第一个像素数据的地址。它也可以看成是位图中的第一个扫描行
System.IntPtr srcPtr
= srcBmData.Scan0;
System.IntPtr dstPtr
= dstBmData.Scan0;
//将Bitmap对象的信息存放到byte数组中
int src_bytes
= srcBmData.Stride * height;
byte[]
srcValues = new byte[src_bytes];
int dst_bytes
= dstBmData.Stride * height;
byte[]
dstValues = new byte[dst_bytes];
//复制GRB信息到byte数组
System.Runtime.InteropServices.Marshal.Copy(srcPtr,
srcValues, 0, src_bytes);
System.Runtime.InteropServices.Marshal.Copy(dstPtr,
dstValues, 0, dst_bytes);
//根据Y=0.299*R+0.114*G+0.587B,Y为亮度
for (int i
= 0; i < height; i++)
for (int j
= 0; j < wide; j++)
{
//只处理每行中图像像素数据,舍弃未用空间
//注意位图结构中RGB按BGR的顺序存储
int k
= 3 * j;
byte temp
= (byte)(srcValues[i *
srcBmData.Stride + k + 2] * .299
+
srcValues[i * srcBmData.Stride + k + 1] * .587 + srcValues[i *
srcBmData.Stride + k] * .114);
dstValues[i
* dstBmData.Stride + j] = temp;
}
//将更改过的byte[]拷贝到原位图
System.Runtime.InteropServices.Marshal.Copy(dstValues,
0, dstPtr, dst_bytes);
//解锁位图
srcBitmap.UnlockBits(srcBmData);
dstBitmap.UnlockBits(dstBmData);
return dstBitmap;
}//#
四指针法
C/C++的习惯,不是C#的特点
publicstatic Bitmap RGB2Gray(Bitmap srcBitmap)
{
int wide
= srcBitmap.Width;
int height
= srcBitmap.Height ;
Rectangle rect
= new Rectangle(0, 0, wide, height);
BitmapData srcBmData
= srcBitmap.LockBits(rect,
ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
Bitmap dstBitmap
= CreateGrayscaleImage(wide, height);
BitmapData dstBmData
= dstBitmap.LockBits(rect,
ImageLockMode.ReadWrite, PixelFormat.Format8bppIndexed);
System.IntPtr srcScan =
srcBmData.Scan0;
System.IntPtr dstScan =
dstBmData.Scan0;
Unsafe //启动不安全代码
{
byte*
srcP = (byte*)(void*) srcScan;
byte*
dstP = (byte*)(void*) dstScan;
int srcOffset
= srcBmData.Stride - wide * 3;
int dstOffset
= dstBmData.Stride - wide ;
byte red,
green, blue;
for (int y
= 0; y < height; y++)
{
for (int x
= 0; x
{
blue = srcP [0];
green = srcP [1];
red = srcP [2];
* dstP = (byte)(.299 * red + .587
* green + .114 * blue);
}
srcP += srcOffset;
dstP += dstOffset;
}
}
srcBitmap.UnlockBits(srcBmData);
dstBitmap.UnlockBits(dstBmData );
return dstBitmap;
}//#
五.矩阵法
并不是什么新方法,只是将图像数据分做R,G,B三个矩阵(二维数组)存储,类似MATLAB的习惯.
publicstatic bool GetRGB(Bitmap Source, out int[,]
R, out int[,] G, out int[,] B)
{
try
{
int iWidth
= Source.Width;
int iHeight
= Source.Height;
Rectangle rect
= new Rectangle(0, 0, iWidth, iHeight);
System.Drawing.Imaging.BitmapData bmpData =
Source.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadWrite,
Source.PixelFormat);
IntPtr iPtr
= bmpData.Scan0;
int iBytes
= iWidth * iHeight * 3;
byte[]
PixelValues = new byte[iBytes];
System.Runtime.InteropServices.Marshal.Copy(iPtr, PixelValues, 0,
iBytes);
Source.UnlockBits(bmpData);
//注意这个地方图像的两维方向与数组两维的方向是转置的关系
R = new int[iHeight, iWidth];
G = new int[iHeight, iWidth];
B = new int[iHeight, iWidth];
int iPoint
= 0;
for (int i
= 0; i < iHeight; i++)
{
for (int j
= 0; j < iWidth; j++)
{
//注意,Windows
中三基色的排列顺序是 BGR 而不是 RGB!
B[i, j] = Convert.ToInt32(PixelValues[iPoint++]);
G[i, j] = Convert.ToInt32(PixelValues[iPoint++]);
R[i, j] = Convert.ToInt32(PixelValues[iPoint++]);
}
}
return true;
}
catch (Exception)
{
R = null;
G = null;
B = null;
return false;
}
}//#
public static Bitmap FromRGB(int[,]
R, int[,]
G, int[,]
B)
{
int iWidth
= G.GetLength(1);
int iHeight
= G.GetLength(0);
Bitmap Result
= new Bitmap(iWidth, iHeight,
System.Drawing.Imaging.PixelFormat.Format24bppRgb);
Rectangle rect
= new Rectangle(0, 0, iWidth, iHeight);
System.Drawing.Imaging.BitmapData bmpData =
Result.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadWrite,
System.Drawing.Imaging.PixelFormat.Format24bppRgb);
IntPtr iPtr
= bmpData.Scan0;
int iStride
= bmpData.Stride;
int iBytes
= iWidth * iHeight * 3;
byte[]
PixelValues = new byte[iBytes];
int iPoint
= 0;
for (int i
= 0; i < iHeight; i++)
for (int j
= 0; j < iWidth; j++)
{
int iG
= G[i, j];
int iB
= B[i, j];
int iR
= R[i, j];
PixelValues[iPoint] = Convert.ToByte(iB);
PixelValues[iPoint + 1] = Convert.ToByte(iG);
PixelValues[iPoint + 2] = Convert.ToByte(iR);
iPoint += 3;
}
System.Runtime.InteropServices.Marshal.Copy(PixelValues, 0, iPtr,
iBytes);
Result.UnlockBits(bmpData);
return Result;
}//#
public static bool GetGray(Bitmap srcBitmap, out byte [,]
gray)
{
Bitmap tempBitmap;
if (srcBitmap.PixelFormat
!= PixelFormat.Format8bppIndexed)
tempBitmap = ImageProcess.Image.Gray(srcBitmap);
else
tempBitmap = srcBitmap;
int wide
= tempBitmap.Width;
int height
= tempBitmap.Height;
gray = new byte [height,
wide];
BitmapData gbmData
= tempBitmap.LockBits(new Rectangle(0, 0, wide, height),
ImageLockMode.ReadWrite, PixelFormat.Format8bppIndexed);
System.IntPtr ScanG =
gbmData.Scan0;
int gOffset
= gbmData.Stride - wide;
unsafe
{
byte*
g = (byte*)(void*)ScanG;
//
for each pixel
for (int y
= 0; y < height; y++)
{
//
for each pixel
for (int x
= 0; x < wide; x++, g++)
{
gray[y ,x ] =*g;
}
g += gOffset;
}
}
tempBitmap.UnlockBits(gbmData);
return true ;
}//#
public static Bitmap FromGray(byte [,]
Gray)
{
int iWidth
= Gray.GetLength(1);
int iHeight
= Gray.GetLength(0);
Bitmap dstBitmap
= ImageProcess.Image.CreateGrayscaleImage(iWidth,
iHeight);
BitmapData gbmData
= dstBitmap.LockBits(new Rectangle(0, 0, iWidth, iHeight),
ImageLockMode.ReadWrite, PixelFormat.Format8bppIndexed);
System.IntPtr ScanG =
gbmData.Scan0;
int gOffset
= gbmData.Stride - iWidth;
unsafe
{
byte*
g = (byte*)(void*)ScanG;
//注意这个地方图像的两维方向与数组两维的方向是转置的关系
for (int i
= 0; i < iHeight; i++)
{
for (int j
= 0; j < iWidth; j++)
{
*g=(byte )Gray[i, j]
;
g++;
}
g += gOffset;
}
}
dstBitmap.UnlockBits(gbmData);
return dstBitmap;
} //#
///
///Create
and initialize grayscale image
///
public static Bitmap CreateGrayscaleImage( int width, int height
)
{
//
create new image
Bitmap bmp
= new Bitmap( width,
height, PixelFormat.Format8bppIndexed );
//
set palette to grayscale
SetGrayscalePalette( bmp );
//
return new image
return bmp;
}//#
///
///Set
pallete of the image to grayscale
///
public static void SetGrayscalePalette( Bitmap srcImg
)
{
//
check pixel format
if (
srcImg.PixelFormat != PixelFormat.Format8bppIndexed )
throw new ArgumentException(
);
//
get palette
ColorPalette cp
= srcImg.Palette;
//
init palette
for ( int i
= 0; i < 256; i++ )
{
cp.Entries[i] = Color.FromArgb( i, i, i );
}
//
set palette back
srcImg.Palette = cp;
}//#
/*********************************END********************************************