浅谈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属性为每行像素所占的字节。