1. GetPixel方法和SetPixel方法:和設置一個圖像的指定像素的顏色.
2. PixelFormat屬性:返回圖像的像素格式.
3. Palette屬性:獲取和設置圖像所使用的顏色調色板.
4. Height Width屬性:返回圖像的高度和寬度.
5. LockBits方法和UnlockBits方法:分別鎖定和解鎖系統內存中的位圖像素.
1. Height屬性:被鎖定位圖的高度.
2. Width屬性:被鎖定位圖的高度.
3. PixelFormat屬性:數據的實際像素格式.
4. Scan0屬性:被鎖定數組的首字節地址,如果整個圖像被鎖定,則是圖像的第一個字節地址.
5. Stride屬性:步幅,也稱為掃描寬度.
這涉及到位圖的本身結構,系統要保證每行的字節數必須為4的倍數.
這個類提供了位元圖和位元文件操作的函數.
thmbnail.Save(“c://thumnail.gif“, System.Drawing.Imaging.ImageFormat.Gif);
一個批量處理圖片的軟件,包括各種處理方式,處理效果,但是在保存為gif的時候出現了問題,在網上查了很久也沒有發現一個可用的改善gif圖片質量的方法,找到了一個解決辦法,保存出來的gif容量大減,但是效果基本符合常規這中方法就是就是“Octree“ 算法。
“Octree“ 算法允許我們插入自己的算法來量子化我們的圖像。
一個好的“顏色量子化”算法應該考慮在兩個像素顆粒之間填充與這兩個像素顏色相近的過渡顏色,提供更多可視顏色空間。
Morgan Skinner提供了很好的“Octree“ 算法代碼,大家可以下載參考使用。
使用OctreeQuantizer很方便:
System.Drawing.Bitmap b = new System.Drawing.Bitmap(“c://original_image.gif“);
System.Drawing.Image thmbnail = b.GetThumbnailImage(100,75,null,new IntPtr());
OctreeQuantizer quantizer = new OctreeQuantizer ( 255 , 8 ) ;
using ( Bitmap quantized = quantizer.Quantize ( thmbnail ) )
{
quantized.Save(“c://thumnail.gif“, System.Drawing.Imaging.ImageFormat.Gif);
}
OctreeQuantizer grayquantizer = new GrayscaleQuantizer ( ) ;
using ( Bitmap quantized = grayquantizer.Quantize ( thmbnail ) )
{
quantized.Save(“c://thumnail.gif“, System.Drawing.Imaging.ImageFormat.Gif);
}
你可以
點擊這裡下載類的文件(項目文件 ),根據我的試用,只需要兩個類文件(OctreeQuantizer.cs,Quantizer.cs)即可運行,將這兩個類文件的namespace改成
你項目的名稱就行,還有,需要在不安全編譯的方式下編譯,右擊項目名稱,在生成選項卡里選擇"允許不安全代碼"即可
//窗口重繪,在窗體上顯示圖像,重載Paint
privatevoid frmMain_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
{
if (showBitmap != null)
{
Graphics g = e.Graphics;
g.DrawImage(showBitmap, newRectangle(this.AutoScrollPosition.X, this.AutoScrollPosition.Y ,
(int)(showBitmap.Width), (int)(showBitmap.Height)));
}
}
//灰度化
privatevoid menu2Gray_Click(object sender, EventArgs e)
{
if (showBitmap == null) return;
showBitmap = RGB2Gray(showBitmap);//下面都以RGB2Gray為例
this.Invalidate();
}
二. 提取像素法(超級無敵慢)
即用C#中的getPixel和setPixel 來做取得和設定像素。
這種方法簡單易懂,但相當耗時,完全不可取.
public staticBitmap
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 ;
}
三. 內存法(速度較快)
大概的步驟如下:
先建立一個rect,這是用來放圖的框框
public static Bitmap
RGB2Gray(Bitmap srcBitmap)
{
int wide = srcBitmap.Width;
int height = srcBitmap.Height;
Rectangle rect = newRectangle(0, 0, wide,
height);
//將srcBitmap鎖定到系統內的記憶體的某個區塊中,並將這個結果交給BitmapData類別的srcBimap
BitmapData srcBmData =
srcBitmap.LockBits(rect,ImageLockMode.ReadWrite,
PixelFormat.Format24bppRgb);
//將CreateGrayscaleImage灰階影像,並將這個結果交給Bitmap類別的dstBimap
Bitmap dstBitmap =
CreateGrayscaleImage(wide, height);//這個函數在後面有定義
//將dstBitmap鎖定到系統內的記憶體的某個區塊中,並將這個結果交給BitmapData類別的dstBimap
BitmapData dstBmData =
dstBitmap.LockBits(rect,ImageLockMode.ReadWrite,
PixelFormat.Format8bppIndexed);
//位元圖中第一個像素數據的地址。它也可以看成是位圖中的第一個掃描行
//目的是設兩個起始旗標srcPtr、dstPtr,為srcBmData、dstBmData的掃描行的開始位置
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;
}
System.Runtime.InteropServices.Marshal.Copy(dstValues, 0, dstPtr, dst_bytes);
//解鎖位圖
srcBitmap.UnlockBits(srcBmData);
dstBitmap.UnlockBits(dstBmData);
return dstBitmap;
}
四 指標法(速度最快)
C/C++的習慣,不是C#的特點,但是效率奇高
public static Bitmap
RGB2Gray(Bitmap srcBitmap)
{
int wide = srcBitmap.Width;
int height = srcBitmap.Height ;
Rectangle rect = newRectangle(0, 0, wide,
height);
//將srcBitmap鎖定到系統內的記憶體的某個區塊中,並將這個結果交給BitmapData類別的srcBimap
BitmapData srcBmData =
srcBitmap.LockBits(rect,ImageLockMode.ReadWrite,
PixelFormat.Format24bppRgb);
//將CreateGrayscaleImage灰階影像,並將這個結果交給Bitmap類別的dstBimap
Bitmap dstBitmap =
CreateGrayscaleImage(wide, height);
//將dstBitmap鎖定到系統內的記憶體的某個區塊中,並將這個結果交給BitmapData類別的dstBimap
BitmapData dstBmData =
dstBitmap.LockBits(rect,ImageLockMode.ReadWrite,
PixelFormat.Format8bppIndexed);
//位元圖中第一個像素數據的地址。它也可以看成是位圖中的第一個掃描行
//目的是設兩個起始旗標srcPtr、dstPtr,為srcBmData、dstBmData的掃描行的開始位置
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 <wide ; x++, srcP += 3,
dstP++)
{
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的習慣.
參閱出處連結
C# 數字圖像處理的3 種典型方法( 精簡版)
C#數字圖像處理有3種典型方法:提取像素法、內存法、指標法。
其中提取像素法使用的是GDI+中的Bitmap.GetPixel和Bitmap.SetPixel方法;
內存法是通過LockBits方法來獲取位圖的首位址,從而把圖像數據直接複製到內存中進行處理;
指標法與內存法相似,但該方法直接應用指標對應位置圖進行操作,由於在默認情況下,C#不支持指標運算,所以該方法只能在unsafe關鍵字所標記的代碼塊中使用。
以一幅真彩色圖像的灰度化為例,下面代碼分別展現了這3種方法的使用,
方便學習圖像處理的基本技巧。
(1) 像素提取法
if (curBitmap != null)
{
Color curColor;
int gray;
for (int i = 0; i < curBitmap.Width; i++)
{
for (int j = 0; j < curBitmap.Height; j++)
{
curColor = curBitmap.GetPixel(i, j);
gray = (int)(0.3 * curColor.R + 0.59 * curColor.G * 0.11 * curColor.B);
curBitmap.SetPixel(i, j, curColor);
}
}
}
(2) 內存法
if (curBitmap != null)
{
int width = curBitmap.Width;
int height = curBitmap.Height;
int length = height * 3 * width;
RGB = new byte[length];
BitmapData data = curBitmap.LockBits(new Rectangle(0, 0, width, height),
ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
System.IntPtr Scan0 = data.Scan0;
System.Runtime.InteropServices.Marshal.Copy(Scan0, RGB, 0, length);
double gray = 0;
for (int i = 0; i < RGB.Length; i=i+3)
{
gray = RGB[i + 2] * 0.3 + RGB[i + 1] * 0.59 + RGB[i] * 0.11;
RGB[i + 2] = RGB[i + 1] = RGB[i] = (byte)gray;
}
System.Runtime.InteropServices.Marshal.Copy(RGB, 0, Scan0, length);
curBitmap.UnlockBits(data);
}
(3) 指標法
if (curBitmap != null)
{
int width = curBitmap.Width;
int height = curBitmap.Height;
BitmapData data = curBitmap.LockBits(new Rectangle(0, 0, width, height),
ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
System.IntPtr Scan0 = data.Scan0;
int stride = data.Stride;
System.Runtime.InteropServices.Marshal.Copy(Scan0, RGB, 0, length);
unsafe
{
byte* p = (byte*)Scan0;
int offset = stride - width * 3;
double gray = 0;
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
gray = 0.3 * p[2] + 0.59 * p[1] + 0.11 * p[0];
p[2] = p[1] = p[0] = (byte)gray;
p += 3;
}
p += offset;
}
}
curBitmap.UnlockBits(data);
}
在以上3種方法中:
提取像素法能直觀的展示圖像處理過程,可讀性很好,但效率最低,並不適合做圖像處理方面的工程應用;
內存法把圖像直接複製到內存中,直接對內存中的數據進行處理,速度明顯提高,程式難度也不大;
指標法直接應用指標對圖像進行處理,所以速度最快。
三.小結:
本文通過一個簡單的實例向大家展現了用Visual C#以及GDI+完成數字圖像處理的基本方法,
通過實例,我們不難發現合理運用新技術不僅可以大大簡化我們的編程工作,
還可以提高編程的效率。
不過我們在運用新技術的同時也得明白掌握基本的編程思想才是最主要的,
不同的語言、不同的機制只是實現的具體方式不同而已,其內在的思想還是相通的。
對於上面的例子,掌握了編寫圖像處理函數的算法,用其他的方式實現也應該是可行的。
同時,在上面的基礎上,讀者不妨試著舉一反三,編寫出更多的圖像處理的函數來,
以充實並完善這個簡單的實例。
image to byte[] MemoryStream ms=new MemoryStream();
byte[] imagedata=null;
pictureBox1.Image.Save(ms,System.Drawing.Imaging.ImageFormat.Gif );
imagedata=ms.GetBuffer ();
byte[] to image ms = New IO.MemoryStream(by)img = Drawing.Image.FromStream(ms)
留言