欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

浅谈Visual C#进行图像处理(读取、保存以及对像素的访问)

程序员文章站 2023-09-08 10:13:04
这里之所以说“浅谈”是因为我这里只是简单的介绍如何使用visual c#进行图像的读入、保存以及对像素的访问。而不涉及太多的算法。 一、读取图像 在visual...

这里之所以说“浅谈”是因为我这里只是简单的介绍如何使用visual c#进行图像的读入、保存以及对像素的访问。而不涉及太多的算法。

一、读取图像

在visual c#中我们可以使用一个picture box控件来显示图片,如下:

复制代码 代码如下:

private void btnopenimage_click(object sender, eventargs e)
{
    openfiledialog ofd = new openfiledialog();
    ofd.filter = "bmp files(*.bmp)|*.bmp|jpg files(*.jpg;*.jpeg)|*.jpg;*.jpeg|all files(*.*)|*.*";
    ofd.checkfileexists = true;
    ofd.checkpathexists = true;
    if (ofd.showdialog() == dialogresult.ok)
    {
        //pbxshowimage.imagelocation = ofd.filename;
        bmp = new bitmap(ofd.filename);
        if (bmp==null)
        {
            messagebox.show("加载图片失败!", "错误");
            return;
        }
        pbxshowimage.image = bmp;
        ofd.dispose();
    }
}

其中bmp为类的一个对象:private bitmap bmp=null;
在使用bitmap类和bitmapdata类之前,需要使用using system.drawing.imaging;

二、保存图像

复制代码 代码如下:

private void btnsaveimage_click(object sender, eventargs e)
{
    if (bmp == null) return;
    savefiledialog sfd = new savefiledialog();
    sfd.filter = "bmp files(*.bmp)|*.bmp|jpg files(*.jpg;*.jpeg)|*.jpg;*.jpeg|all files(*.*)|*.*";
    if (sfd.showdialog() == dialogresult.ok)
    {
        pbxshowimage.image.save(sfd.filename);
        messagebox.show("保存成功!","提示");
        sfd.dispose();
    }
}

三、对像素的访问

我们可以来建立一个graybitmapdata类来做相关的处理。整个类的程序如下:

复制代码 代码如下:

using system;
using system.collections.generic;
using system.linq;
using system.text;
using system.drawing;
using system.drawing.imaging;
using system.windows.forms;
namespace imageelf
{
    class graybitmapdata
    {
        public byte[,] data;//保存像素矩阵
        public int width;//图像的宽度
        public int height;//图像的高度
        public graybitmapdata()
        {
            this.width = 0;
            this.height = 0;
            this.data = null;
        }
        public graybitmapdata(bitmap bmp)
        {
            bitmapdata bmpdata = bmp.lockbits(new rectangle(0, 0, bmp.width, bmp.height), imagelockmode.readonly, pixelformat.format24bpprgb);
            this.width = bmpdata.width;
            this.height = bmpdata.height;
            data = new byte[height, width];
            unsafe
            {
                byte* ptr = (byte*)bmpdata.scan0.topointer();
                for (int i = 0; i < height; i++)
                {
                    for (int j = 0; j < width; j++)
                    {
    //将24位的rgb彩色图转换为灰度图
                        int temp = (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);
        }
        public graybitmapdata(string path)
            : this(new bitmap(path))
        {
        }
        public bitmap tobitmap()
        {
            bitmap bmp=new bitmap(width,height,pixelformat.format24bpprgb);
            bitmapdata bmpdata=bmp.lockbits(new rectangle(0,0,width,height),imagelockmode.writeonly,pixelformat.format24bpprgb);
            unsafe
            {
                byte* ptr=(byte*)bmpdata.scan0.topointer();
                for(int i=0;i<height;i++)
                {
                    for(int j=0;j<width;j++)
                    {
                        *(ptr++)=data[i,j];
                        *(ptr++)=data[i,j];
                        *(ptr++)=data[i,j];
                    }
                    ptr+=bmpdata.stride-width*3;
                }
            }
            bmp.unlockbits(bmpdata);
            return bmp;
        }
        public void showimage(picturebox pbx)
        {
            bitmap b = this.tobitmap();
            pbx.image = b;
            //b.dispose();
        }
        public void saveimage(string path)
        {
            bitmap b=tobitmap();
            b.save(path);
            //b.dispose();
        }
//均值滤波
        public void averagefilter(int windowsize)
        {
            if (windowsize % 2 == 0)
            {
                return;
            }
            for (int i = 0; i < height; i++)
            {
                for (int j = 0; j < width; j++)
                {
                    int sum = 0;
                    for (int g = -(windowsize - 1) / 2; g <= (windowsize - 1) / 2; g++)
                    {
                        for (int k = -(windowsize - 1) / 2; k <= (windowsize - 1) / 2; k++)
                        {
                            int a = 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));
                }
            }
        }
//中值滤波
        public void midfilter(int windowsize)
        {
            if (windowsize % 2 == 0)
            {
                return;
            }
            int[] temp = new int[windowsize * windowsize];
            byte[,] newdata = new byte[height, width];
            for (int i = 0; i < height; i++)
            {
                for (int j = 0; j < width; j++)
                {
                    int n = 0;
                    for (int g = -(windowsize - 1) / 2; g <= (windowsize - 1) / 2; g++)
                    {
                        for (int k = -(windowsize - 1) / 2; k <= (windowsize - 1) / 2; k++)
                        {
                            int a = 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 (int i = 0; i < height; i++)
            {
                for (int j = 0; j < width; j++)
                {
                    data[i, j] = newdata[i, j];
                }
            }
        }
//获得一个向量的中值
        private byte getmidvalue(int[] t, int length)
        {
            int temp = 0;
            for (int i = 0; i < length - 2; i++)
            {
                for (int j = 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];
        }
//一种新的滤波方法,是亮的更亮、暗的更暗
        public void newfilter(int windowsize)
        {
            if (windowsize % 2 == 0)
            {
                return;
            }
            for (int i = 0; i < height; i++)
            {
                for (int j = 0; j < width; j++)
                {
                    int sum = 0;
                    for (int g = -(windowsize - 1) / 2; g <= (windowsize - 1) / 2; g++)
                    {
                        for (int k = -(windowsize - 1) / 2; k <= (windowsize - 1) / 2; k++)
                        {
                            int a = 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];
                        }
                    }
                    double avg = (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);
                    }
                }
            }
        }
//直方图均衡
        public void histequal()
        {
            double[] num = new double[256] ;
            for(int i=0;i<256;i++) num[i]=0;
            for (int i = 0; i < height; i++)
            {
                for (int j = 0; j < width; j++)
                {
                    num[data[i, j]]++;
                }
            }
            double[] newgray = new double[256];
            double n = 0;
            for (int i = 0; i < 256; i++)
            {
                n += num[i];
                newgray[i] = n * 255 / (height * width);
            }
            for (int i = 0; i < height; i++)
            {
                for (int j = 0; j < width; j++)
                {
                    data[i,j]=(byte)newgray[data[i,j]];
                }
            }
        }
}
}

在graybitmapdata类中,只要我们对一个二维数组data进行一系列的操作就是对图片的操作处理。在窗口上,我们可以使用
一个按钮来做各种调用:

复制代码 代码如下:

//均值滤波
private void btnavgfilter_click(object sender, eventargs e)
{
    if (bmp == null) return;
    graybitmapdata gbmp = new graybitmapdata(bmp);
    gbmp.averagefilter(3);
    gbmp.showimage(pbxshowimage);
}
//转换为灰度图
private void btntogray_click(object sender, eventargs e)
{
    if (bmp == null) return;
    graybitmapdata gbmp = new graybitmapdata(bmp);
    gbmp.showimage(pbxshowimage);
}

四、总结

在visual c#中对图像进行处理或访问,需要先建立一个bitmap对象,然后通过其lockbits方法来获得一个bitmapdata类的对象,然后通过获得其像素数据的首地址来对bitmap对象的像素数据进行操作。当然,一种简单但是速度慢的方法是用bitmap类的getpixel和setpixel方法。其中bitmapdata类的stride属性为每行像素所占的字节。