浅谈Visual C#进行图像处理(读取、保存以及对像素的访问)
这里之所以说“浅谈”是因为我这里只是简单的介绍如何使用VisualC#进行图像的读入、保存以及对像素的访问。而不涉及太多的算法。
一、读取图像
在VisualC#中我们可以使用一个PictureBox控件来显示图片,如下:
privatevoidbtnOpenImage_Click(objectsender,EventArgse) { OpenFileDialogofd=newOpenFileDialog(); ofd.Filter="BMPFiles(*.bmp)|*.bmp|JPGFiles(*.jpg;*.jpeg)|*.jpg;*.jpeg|AllFiles(*.*)|*.*"; ofd.CheckFileExists=true; ofd.CheckPathExists=true; if(ofd.ShowDialog()==DialogResult.OK) { //pbxShowImage.ImageLocation=ofd.FileName; bmp=newBitmap(ofd.FileName); if(bmp==null) { MessageBox.Show("加载图片失败!","错误"); return; } pbxShowImage.Image=bmp; ofd.Dispose(); } }
其中bmp为类的一个对象:privateBitmapbmp=null;
在使用Bitmap类和BitmapData类之前,需要使用usingSystem.Drawing.Imaging;
二、保存图像
privatevoidbtnSaveImage_Click(objectsender,EventArgse) { if(bmp==null)return; SaveFileDialogsfd=newSaveFileDialog(); sfd.Filter="BMPFiles(*.bmp)|*.bmp|JPGFiles(*.jpg;*.jpeg)|*.jpg;*.jpeg|AllFiles(*.*)|*.*"; if(sfd.ShowDialog()==DialogResult.OK) { pbxShowImage.Image.Save(sfd.FileName); MessageBox.Show("保存成功!","提示"); sfd.Dispose(); } }
三、对像素的访问
我们可以来建立一个GrayBitmapData类来做相关的处理。整个类的程序如下:
usingSystem; usingSystem.Collections.Generic; usingSystem.Linq; usingSystem.Text; usingSystem.Drawing; usingSystem.Drawing.Imaging; usingSystem.Windows.Forms; namespaceImageElf { classGrayBitmapData { publicbyte[,]Data;//保存像素矩阵 publicintWidth;//图像的宽度 publicintHeight;//图像的高度 publicGrayBitmapData() { this.Width=0; this.Height=0; this.Data=null; } publicGrayBitmapData(Bitmapbmp) { BitmapDatabmpData=bmp.LockBits(newRectangle(0,0,bmp.Width,bmp.Height),ImageLockMode.ReadOnly,PixelFormat.Format24bppRgb); this.Width=bmpData.Width; this.Height=bmpData.Height; Data=newbyte[Height,Width]; unsafe { byte*ptr=(byte*)bmpData.Scan0.ToPointer(); for(inti=0;i<Height;i++) { for(intj=0;j<Width;j++) { //将24位的RGB彩色图转换为灰度图 inttemp=(int)(0.114*(*ptr++))+(int)(0.587*(*ptr++))+(int)(0.299*(*ptr++)); Data[i,j]=(byte)temp; } ptr+=bmpData.Stride-Width*3;//指针加上填充的空白空间 } } bmp.UnlockBits(bmpData); } publicGrayBitmapData(stringpath) :this(newBitmap(path)) { } publicBitmapToBitmap() { Bitmapbmp=newBitmap(Width,Height,PixelFormat.Format24bppRgb); BitmapDatabmpData=bmp.LockBits(newRectangle(0,0,Width,Height),ImageLockMode.WriteOnly,PixelFormat.Format24bppRgb); unsafe { byte*ptr=(byte*)bmpData.Scan0.ToPointer(); for(inti=0;i<Height;i++) { for(intj=0;j<Width;j++) { *(ptr++)=Data[i,j]; *(ptr++)=Data[i,j]; *(ptr++)=Data[i,j]; } ptr+=bmpData.Stride-Width*3; } } bmp.UnlockBits(bmpData); returnbmp; } publicvoidShowImage(PictureBoxpbx) { Bitmapb=this.ToBitmap(); pbx.Image=b; //b.Dispose(); } publicvoidSaveImage(stringpath) { Bitmapb=ToBitmap(); b.Save(path); //b.Dispose(); } //均值滤波 publicvoidAverageFilter(intwindowSize) { if(windowSize%2==0) { return; } for(inti=0;i<Height;i++) { for(intj=0;j<Width;j++) { intsum=0; for(intg=-(windowSize-1)/2;g<=(windowSize-1)/2;g++) { for(intk=-(windowSize-1)/2;k<=(windowSize-1)/2;k++) { inta=i+g,b=j+k; if(a<0)a=0; if(a>Height-1)a=Height-1; if(b<0)b=0; if(b>Width-1)b=Width-1; sum+=Data[a,b]; } } Data[i,j]=(byte)(sum/(windowSize*windowSize)); } } } //中值滤波 publicvoidMidFilter(intwindowSize) { if(windowSize%2==0) { return; } int[]temp=newint[windowSize*windowSize]; byte[,]newdata=newbyte[Height,Width]; for(inti=0;i<Height;i++) { for(intj=0;j<Width;j++) { intn=0; for(intg=-(windowSize-1)/2;g<=(windowSize-1)/2;g++) { for(intk=-(windowSize-1)/2;k<=(windowSize-1)/2;k++) { inta=i+g,b=j+k; if(a<0)a=0; if(a>Height-1)a=Height-1; if(b<0)b=0; if(b>Width-1)b=Width-1; temp[n++]=Data[a,b]; } } newdata[i,j]=GetMidValue(temp,windowSize*windowSize); } } for(inti=0;i<Height;i++) { for(intj=0;j<Width;j++) { Data[i,j]=newdata[i,j]; } } } //获得一个向量的中值 privatebyteGetMidValue(int[]t,intlength) { inttemp=0; for(inti=0;i<length-2;i++) { for(intj=i+1;j<length-1;j++) { if(t[i]>t[j]) { temp=t[i]; t[i]=t[j]; t[j]=temp; } } } return(byte)t[(length-1)/2]; } //一种新的滤波方法,是亮的更亮、暗的更暗 publicvoidNewFilter(intwindowSize) { if(windowSize%2==0) { return; } for(inti=0;i<Height;i++) { for(intj=0;j<Width;j++) { intsum=0; for(intg=-(windowSize-1)/2;g<=(windowSize-1)/2;g++) { for(intk=-(windowSize-1)/2;k<=(windowSize-1)/2;k++) { inta=i+g,b=j+k; if(a<0)a=0; if(a>Height-1)a=Height-1; if(b<0)b=0; if(b>Width-1)b=Width-1; sum+=Data[a,b]; } } doubleavg=(sum+0.0)/(windowSize*windowSize); if(avg/255<0.5) { Data[i,j]=(byte)(2*avg/255*Data[i,j]); } else { Data[i,j]=(byte)((1-2*(1-avg/255.0)*(1-Data[i,j]/255.0))*255); } } } } //直方图均衡 publicvoidHistEqual() { double[]num=newdouble[256]; for(inti=0;i<256;i++)num[i]=0; for(inti=0;i<Height;i++) { for(intj=0;j<Width;j++) { num[Data[i,j]]++; } } double[]newGray=newdouble[256]; doublen=0; for(inti=0;i<256;i++) { n+=num[i]; newGray[i]=n*255/(Height*Width); } for(inti=0;i<Height;i++) { for(intj=0;j<Width;j++) { Data[i,j]=(byte)newGray[Data[i,j]]; } } } } }
在GrayBitmapData类中,只要我们对一个二维数组Data进行一系列的操作就是对图片的操作处理。在窗口上,我们可以使用
一个按钮来做各种调用:
//均值滤波 privatevoidbtnAvgFilter_Click(objectsender,EventArgse) { if(bmp==null)return; GrayBitmapDatagbmp=newGrayBitmapData(bmp); gbmp.AverageFilter(3); gbmp.ShowImage(pbxShowImage); } //转换为灰度图 privatevoidbtnToGray_Click(objectsender,EventArgse) { if(bmp==null)return; GrayBitmapDatagbmp=newGrayBitmapData(bmp); gbmp.ShowImage(pbxShowImage); }
四、总结
在Visualc#中对图像进行处理或访问,需要先建立一个Bitmap对象,然后通过其LockBits方法来获得一个BitmapData类的对象,然后通过获得其像素数据的首地址来对Bitmap对象的像素数据进行操作。当然,一种简单但是速度慢的方法是用Bitmap类的GetPixel和SetPixel方法。其中BitmapData类的Stride属性为每行像素所占的字节。