浅谈Visual C#进行图像处理

作者:彭军 http://pengjun.org.cn

这里之所以说“浅谈”是因为我这里只是简单的介绍如何使用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属性为每行像素所占的字节。

 

 

 

 

 

 

 

 

 

 

 

C# colorMatrix 对图片的处理 : 亮度调整 抓屏 翻转 随鼠标画矩形

 

1.图片亮度处理

 

        private void btn_Grap_Click(object senderEventArgs e)

        {

            //亮度百分比

            int percent = 50;

            Single v = 0.006F * percent;    

            Single[][] matrix = {         

                new Single[] { 1, 0, 0, 0, 0 },         

                new Single[] { 0, 1, 0, 0, 0 },          

                new Single[] { 0, 0, 1, 0, 0 },         

                new Single[] { 0, 0, 0, 1, 0 },         

                new Single[] { vvv, 0, 1 }     

            };    

            System.Drawing.Imaging.ColorMatrix cm = new System.Drawing.Imaging.ColorMatrix(matrix);

            System.Drawing.Imaging.ImageAttributes attr = newSystem.Drawing.Imaging.ImageAttributes();    

            attr.SetColorMatrix(cm);    

            //Image tmp 

            Image tmp = Image.FromFile("1.png");

 

            this.pictureBox_Src.Image = Image.FromFile("1.png");

 

            Graphics g = Graphics.FromImage(tmp);  

            try  

            {

                Rectangle destRect = new Rectangle(0, 0, tmp.Widthtmp.Height);        

                g.DrawImage(tmpdestRect, 0, 0, tmp.Widthtmp.HeightGraphicsUnit.Pixelattr);    

            }    

            finally    

            {        

                g.Dispose();    

            }

 

            this.pictureBox_Dest.Image = (Image)tmp.Clone();

        }

 

 

2.抓屏将生成的图片显示在pictureBox

 

        private void btn_Screen_Click(object senderEventArgs e)

        {

            Image myImage = new Bitmap(Screen.PrimaryScreen.Bounds.Width,Screen.PrimaryScreen.Bounds.Height);

            Graphics g = Graphics.FromImage(myImage);

            g.CopyFromScreen(new Point(0, 0), new Point(0, 0), newSize(Screen.PrimaryScreen.Bounds.WidthScreen.PrimaryScreen.Bounds.Height));

            //IntPtr dc1 = g.GetHdc();      //此处这两句多余,具体看最后GetHdc()定义

            //g.ReleaseHdc(dc1);           

            g.Dispose();

            this.pictureBox_Src.SizeMode = PictureBoxSizeMode.StretchImage;

            this.pictureBox_Src.Image = myImage;

            myImage.Save("Screen"ImageFormat.Png);

     }

 

3.翻转

 

        private void btn_RotateFlip_Click(object senderEventArgs e)

        {

            this.pictureBox_Src.Image = Image.FromFile("1.png");

 

            Image tmp = Image.FromFile("1.png");

 

            tmp.RotateFlip(RotateFlipType.Rotate90FlipNone);

            this.pictureBox_Dest.Image = tmp;

        }

4.跟随鼠标在 pictureBox的图片上画矩形

        private int intStartX = 0;

        private int intStartY = 0;

        private bool isMouseDraw = false;

 

        private void pictureBox_Src_MouseDown(object senderMouseEventArgs e)

        {

            isMouseDraw = true;

 

            intStartX = e.X;

            intStartY = e.Y;

        }

 

        private void pictureBox_Src_MouseMove(object senderMouseEventArgs e)

        {

            if (isMouseDraw)

            {

                try

                {

                    //Image tmp = Image.FromFile("1.png");

                    Graphics g = this.pictureBox_Src.CreateGraphics();

                    //清空上次画下的痕迹

                    g.Clear(this.pictureBox_Src.BackColor);

                    Brush brush = new SolidBrush(Color.Red);

                    Pen pen = new Pen(brush, 1);

                    pen.DashStyle = DashStyle.Solid;

                    g.DrawRectangle(pennew Rectangle(intStartX > e.X ? e.X : intStartXintStartY > e.Ye.Y : intStartYMath.Abs(e.X - intStartX), Math.Abs(e.Y - intStartY)));

                    g.Dispose();

                    //this.pictureBox_Src.Image = tmp;

                }

                catch (Exception ex)

                {

                    ex.ToString();

                }

            }

        }

 

        private void pictureBox_Src_MouseUp(object senderMouseEventArgs e)

        {

            isMouseDraw = false;

 

            intStartX = 0;

            intStartY = 0;

        }

5.取灰度

 

        private void btn_GetGray_Click(object senderEventArgs e)

        {

            this.pictureBox_Src.Image = Image.FromFile("1.png");

            Bitmap currentBitmap = new Bitmap(this.pictureBox_Src.Image);

            Graphics g = Graphics.FromImage(currentBitmap);

            ImageAttributes ia = new ImageAttributes();

            float[][] colorMatrix =   {    

                new   float[]   {0.299f,   0.299f,   0.299f,   0,   0},

                new   float[]   {0.587f,   0.587f,   0.587f,   0,   0},

                new   float[]   {0.114f,   0.114f,   0.114f,   0,   0},

                new   float[]   {0,   0,   0,   1,   0},

                new   float[]   {0,   0,   0,   0,   1}

            };

            ColorMatrix cm = new ColorMatrix(colorMatrix);

            ia.SetColorMatrix(cmColorMatrixFlag.DefaultColorAdjustType.Bitmap);

            g.DrawImage(currentBitmapnew Rectangle(0, 0, currentBitmap.WidthcurrentBitmap.Height), 0, 0, currentBitmap.WidthcurrentBitmap.HeightGraphicsUnit.Pixelia);

            this.pictureBox_Dest.Image = (Image)(currentBitmap.Clone());

            g.Dispose();

        }

 

 

 

 

Graphics.GetHdc 方法

.NET Framework 4

获取与此 Graphics 关联的设备上下文的句柄。

命名空间:  System.Drawing
程序集:  System.Drawing(在 System.Drawing.dll 中)

语法

[SecurityPermissionAttribute(SecurityAction.LinkDemand, Flags =

SecurityPermissionFlag.UnmanagedCode)]

public IntPtr GetHdc()

返回值

类型:System.IntPtr
与此 Graphics 关联的设备上下文的句柄。

实现

IDeviceContext.GetHdc()

备注


设备上下文是一个基于 GDI 的 Windows 结构,它定义一组图形对象及其关联的特性,以及影响输出的图形模式。 此方法返回该设备上下文(字体除外)。由于未选择字体,使用 GetHdc 方法返回的句柄对 FromHdc方法进行调用将会失败。

GetHdc 方法调用和 ReleaseHdc 方法调用必须成对出现。 在 GetHdc 和 ReleaseHdc 方法对的范围内,通常仅调用 GDI 函数。 在该范围内对 Graphics(它产生 hdc 参数)的 GDI+ 方法的调用因 ObjectBusy错误而失败。 此外,GDI+ 忽略后续操作中对 hdc 参数的 Graphics 所做的所有状态更改。

示例


下面的代码示例设计为与 Windows 窗体一起使用,它需要 PaintEventArgse,即 Paint 事件处理程序的一个参数。 该示例演示如何调用 Windows GDI 函数以执行与 GDI+ Graphics 方法相同的任务。 代码执行下列操作:

  • 为 Windows DLL 文件 gdi32.dll 定义互操作性 DllImportAttribute 特性。 此 DLL 包含所需的GDI 函数。
  • 将该 DLL 中的 Rectangle 函数定义为外部函数。
  • 创建一支红色钢笔。
  • 利用该钢笔,使用 GDI+ DrawRectangle 方法将矩形绘制到屏幕。
  • 定义内部指针类型变量 hdc 并将它的值设置为窗体的设备上下文句柄。
  • 使用 GDI Rectangle 函数将矩形绘制到屏幕。

释放由 hdc 参数表示的设备上下文。

 

public class GDI

{

    [System.Runtime.InteropServices.DllImport("gdi32.dll")]

    internal static extern bool Rectangle(

       IntPtr hdc,

       int ulCornerX, int ulCornerY,

       int lrCornerX, int lrCornerY);

}

 

[System.Security.Permissions.SecurityPermission(

System.Security.Permissions.SecurityAction.LinkDemand, Flags =

System.Security.Permissions.SecurityPermissionFlag.UnmanagedCode)]           

private void GetHdcForGDI1(PaintEventArgs e)

{

    // Create pen.

    Pen redPen = new Pen(Color.Red, 1);

 

    // Draw rectangle with GDI+.

    e.Graphics.DrawRectangle(redPen, 10, 10, 100, 50);

 

    // Get handle to device context.

    IntPtr hdc = e.Graphics.GetHdc();

 

    // Draw rectangle with GDI using default pen.

    GDI.Rectangle(hdc, 10, 70, 110, 120);

 

    // Release handle to device context.

    e.Graphics.ReleaseHdc(hdc);

}

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值