C#基本图像处理

最近没事,有网上看到一篇关于图像处理的文章,觉得很好,结合它上面的原理,自己写了一个C#图像处理的例子。这个DEMO的界面的有两个PictrueBox控件,用来显示图片,一个是源图片,一个是经过转换的目标图片,UI下面部分有一些按钮,每个按钮实现一个转换功能。这个DEMO允许用户拖一张图片到源PictureBox中,然后通过这些功能按钮实现图片的效果转换。这些功能有把图片变成黑白、底片、浮雕、锐化、柔化等效果。首先来一张运行效果图:


这个DEMO主要有以下几个机能点:
 图像处理
 PictureBox的拖拽。
 计算处理时间
 对图像进行缩放处理


1.图像处理

       首先说明一点,图像处理的算法不是我自己的想出来的,也没有必要去想,网上调查一下,很多的。所以算法是网是找的。在些声明一下。

  1.1 黑白效果

原理: 彩色图像处理成黑白效果通常有3种算法:
(1).最大值法: 使每个像素点的 R, G, B 值等于原像素点的 RGB (颜色值) 中最大的一个;
(2).平均值法: 使用每个像素点的 R,G,B值等于原像素点的RGB值的平均值;
(3).加权平均值法: 对每个像素点的 R, G, B值进行加权,R,G,B的系数分别是0.7,0.2,0.1。
       自认为第三种的效果是最好的。

  1.2 底片效果

       原理: GetPixel方法获得每一点像素的值, 然后再使用SetPixel方法将取反后的颜色值设置到对应的点。

  1.3 锐化效果

       原理:突出显示颜色值大(即形成形体边缘)的像素点。

  1.4 浮雕效果

       原理: 对图像像素点的像素值分别与相邻像素点的像素值相减后加上128, 然后将其作为新的像素点的值。

  1.5 柔化效果

       原理: 当前像素点与周围像素点的颜色差距较大时取其平均值。

 

2.PictureBox的拖拽

拖拽是写在UserControlPictureBox类中,该类继承于UserControl,里面有一个PictrueBox,相当于把图片显示,缩放,拖拽封装了。
C#的拖拽还是很简单的。主要用到DragEnter和DragDrop事件和DoDragDrop方法。
  DragEnter
在拖拽源被拖入到拖拽目标时触发,在这个事件处理函数中,要做的事情就是设置DragEventArgs 对象的Effect,这是一个DragDropEffects枚举值。具体请参见MSDN。
  DragDrop
在释放鼠标并且鼠标拖拽目标之内在时发生。这里面可以接受拖拽的数据。
  DoDragDrop
这个函数表示开始一个拖拽事件,一般是在MouseDown或者MouseMove中调用这个函数,这个函数会阻塞线程。


3.计算处理时间

这部分主要用到了QueryPerformanceCounter 和 QueryPerformanceFrequenc y API。这里会涉及到API与C#交互的问题。代码如下:
[DllImport("kernel32.dll")]
private static extern bool QueryPerformanceCounter(ref long lpPerformanceCount);
[DllImport("kernel32.dll")]
private static extern bool QueryPerformanceFrequency(ref long lpFrequency);


4.对图像进行缩放处理

这部分是也是写在UserControlPictureBox类中。由于用户拖入的图片尺寸可能很大,显示在PictrueBox中虽说可以进行缩放显示,但得到的Image对象还是原来图片你的尺寸,所以为了提高转换效率,就要对图片进行等比例缩放。核心代码如下:
Bitmap bitmap = new Bitmap(newWidth, newHeight, oldImage.PixelFormat);
    Graphics g = Graphics.FromImage(bitmap);
    g.Clear(Color.Transparent);
    g.DrawImage(oldImage, new RectangleF(0, 0, newWidth, newHeight));
return Image.FromHbitmap(bitmap.GetHbitmap());

其中newWidth, newHeight是新的图片的尺寸,这两个值的得到很简单。

 

5.总体说明

界面上有很多按钮,其实每个按钮的事件处理程序都是一个,我在程序中定义了一个枚举:
public enum ImageEffect
{
    GrayScale   = 0,      // 黑白
    Film        = 1,      // 底片
    Relief      = 2,      // 浮雕
    Soften      = 3,      // 柔化
    Sharpen     = 4,      // 锐化
    Canvas      = 5,      // 油画
}

在按钮处理程序中根据不同的按钮ID,给ImageEffectManager类的ChangeEffect方法传递不同的参数。ChangeEffect方法的定义如下:

public void ChangeEffect(ImageEffect effect)
{
    switch(effect)
    {
    case  ImageEffect .GrayScale :
         break;
    }
}

6. 代码

ImageEffectManager.cs类 图像效果管理

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
namespace ImageEffectSample.Classes
{
    public enum ImageEffect
    {
        GrayScale = 0,      // 黑白
        Film      = 1,      // 底片
        Relief    = 2,      // 浮雕
        Soften    = 3,      // 柔化
        Sharpen   = 4,      // 锐化
        Canvas    = 5,      // 油画
    }
    public class ImageEffectManager
    {
        private Bitmap newBitmap = null;
        private Bitmap oldBitmap = null;
        #region Properties
       
        public Bitmap ConvertedBitmap
        {
            get { return this.newBitmap; }
        }
        public Bitmap OriginalBitmap
        {
            set
            {
                DisposeBitmap();
                this.oldBitmap = value;
            }
        }
        public Size PixelSize
        {
            get
            {
                if (this.oldBitmap != null)
                {
                    GraphicsUnit unit = GraphicsUnit.Pixel;
                    RectangleF bounds = this.oldBitmap.GetBounds(ref unit);
                    return new Size(
                        (int)bounds.Width, (int)bounds.Height);
                }
                return new Size(0, 0);
            }
        }
        #endregion
        #region Methods
       
        public ImageEffectManager()
        {
        }
        public void DisposeBitmap()
        {
            if (this.newBitmap != null)
            {
                this.newBitmap.Dispose();
                this.newBitmap = null;
            }
        }
        public void ChangeEffect(ImageEffect effect)
        {
            if (null == this.oldBitmap)
            {
                return;
            }
            Size size = PixelSize;
            int width = size.Width;
            int height = size.Height;
            this.DisposeBitmap();
            this.newBitmap = new Bitmap(width, height);
            switch (effect)
            {
                case ImageEffect.GrayScale:
                    MakeGrayScale(width, height);
                    break;
                case ImageEffect.Film:
                    MakeFilmEffect(width, height);
                    break;
                case ImageEffect.Relief:
                    MakeReliefEffect(width, height);
                    break;
                case ImageEffect.Soften:
                    MakeSoftenEffect(width, height);
                    break;
                case ImageEffect.Sharpen:
                    MakeSharpenEffect(width, height);
                    break;
            }
        }
        private void MakeGrayScale(int width, int height)
        {
            Color c;
            for (int x = 0; x < width; x++)
            {
                for (int y = 0; y < height; y++)
                {
                    c = this.oldBitmap.GetPixel(x, y);
                    ///
                    //
                    // Average
                    // int value = (c.R + c.G + c.B) / 3;
                    //
                    ///
                    // Weighted average
                    int value = (int)(0.7 * c.R) + 
                        (int)(0.2 * c.G) + (int)(0.1 * c.B);
                    this.newBitmap.SetPixel(x, y,
                        Color.FromArgb(c.A, value, value, value));
                }
            }
        }
        private void MakeFilmEffect(int width, int height)
        {
            Color c;
            for (int x = 0; x < width; x++)
            {
                for (int y = 0; y < height; y++)
                {
                    c = this.oldBitmap.GetPixel(x, y);
                    this.newBitmap.SetPixel(x, y, 
                        Color.FromArgb(c.A, 255 - c.R, 
                            255 - c.G, 255 - c.B));
                }
            }
        }
        private void MakeReliefEffect(int width, int height)
        {
            Color c1;
            Color c2;
            int r, g, b = 0;
            for (int x = 0; x < width - 1; x++)
            {
                for (int y = 0; y < height - 1; y++)
                {
                    c1 = this.oldBitmap.GetPixel(x, y);
                    c2 = this.oldBitmap.GetPixel(x + 1, y + 1);
                    r = Math.Abs(c1.R - c2.R + 128);
                    g = Math.Abs(c1.G - c2.G + 128);
                    b = Math.Abs(c1.B - c2.B + 128);
                    r = (r > 255) ? 255 : ((r < 0) ? 0 : r);
                    g = (g > 255) ? 255 : ((g < 0) ? 0 : g);
                    b = (b > 255) ? 255 : ((b < 0) ? 0 : b);
                    this.newBitmap.SetPixel(x, y, 
                        Color.FromArgb(c1.A, r, g, b));
                }
            }
        }
        private void MakeSoftenEffect(int width, int height)
        {
            // The template of Gauss
            int[] Gauss = { 1, 2, 1, 2, 4, 2, 1, 2, 1 };
            Color pixel;
            for (int x = 1; x < width - 1; x++)
            {
                for (int y = 1; y < height - 1; y++)
                {
                    int r = 0, g = 0, b = 0;
                    int Index = 0;
                    for (int col = -1; col <= 1; col++)
                    {
                        for (int row = -1; row <= 1; row++)
                        {
                            pixel = 
                                this.oldBitmap.GetPixel(x + row, y + col);
                            r += pixel.R * Gauss[Index];
                            g += pixel.G * Gauss[Index];
                            b += pixel.B * Gauss[Index];
                            Index++;
                        }
                    }
                    r /= 16;
                    g /= 16;
                    b /= 16;
                    //处理颜色值溢出
                    r = r > 255 ? 255 : r;
                    r = r < 0 ? 0 : r;
                    g = g > 255 ? 255 : g;
                    g = g < 0 ? 0 : g;
                    b = b > 255 ? 255 : b;
                    b = b < 0 ? 0 : b;
                    this.newBitmap.SetPixel(x - 1, y - 1, 
                        Color.FromArgb(r, g, b));
                }
            }
        }
        private void MakeSharpenEffect(int width, int height)
        {
            Color pixel;
            //拉普拉斯模板
            int[] Laplacian ={ -1, -1, -1, -1, 9, -1, -1, -1, -1 };
            for (int x = 1; x < width - 1; x++)
            {
                for (int y = 1; y < height - 1; y++)
                {
                    int r = 0, g = 0, b = 0;
                    int Index = 0;
                    for (int col = -1; col <= 1; col++)
                    {
                        for (int row = -1; row <= 1; row++)
                        {
                            pixel = this.oldBitmap.GetPixel(x + row, y + col);
                            r += pixel.R * Laplacian[Index];
                            g += pixel.G * Laplacian[Index];
                            b += pixel.B * Laplacian[Index];
                            Index++;
                        }
                    }
                    //处理颜色值溢出
                    r = r > 255 ? 255 : r;
                    r = r < 0 ? 0 : r;
                    g = g > 255 ? 255 : g;
                    g = g < 0 ? 0 : g;
                    b = b > 255 ? 255 : b;
                    b = b < 0 ? 0 : b;
                    this.newBitmap.SetPixel(x - 1, y - 1, 
                        Color.FromArgb(r, g, b));
                }
            }
        }
        private void MakeCanvasEffect(int width, int height)
        {
        }
        #endregion
    }
}

TimeCounter.cs类,用于计算处理时间

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
namespace ImageEffectSample.Classes
{
    public class TimeCounter
    {
        private static long startCount = 0;
        private static long elapsedCount = 0;
        #region Properties
        public static float Seconds
        {
            get
            {
                long freq = 0;
                float retValue = 0.0f;
                QueryPerformanceFrequency(ref freq);
                if (freq != 0)
                {
                    retValue = (float)elapsedCount / (float)freq;
                }
                return retValue;
            }
        }
        #endregion
        #region Methods
        public static void Start()
        {
            startCount = 0;
            QueryPerformanceCounter(ref startCount);
        }
        public static void Stop()
        {
            long stopCount = 0;
            QueryPerformanceCounter(ref stopCount);
            elapsedCount = (stopCount - startCount);
        }
        #endregion
        #region Import API
        [DllImport("kernel32.dll")]
        private static extern bool QueryPerformanceCounter(
            ref long lpPerformanceCount);
        [DllImport("kernel32.dll")]
        private static extern bool QueryPerformanceFrequency(
            ref long lpFrequency);
       
        #endregion
    }
}


UserControlPictureBox.cs类  封装了图像缩放,拖拽,显示等功能

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Drawing.Imaging;
namespace ImageEffectSample.UserControls
{
    public partial class UserControlPictureBox : UserControl
    {
        private bool m_dragDropEnable = true;
        private bool m_isOriginalSize = false;
        #region Properties
        public PictureBox PictrueBoxObject
        {
            get { return this.pictureBox; }
        }
        public bool DragDropEnable
        {
            get { return this.m_dragDropEnable; }
            set { m_dragDropEnable = value; }
        }
        public bool IsOriginalSize
        {
            get { return this.m_isOriginalSize; }
            set { m_isOriginalSize = value; }
        }
        #endregion
        #region Methods
       
        public UserControlPictureBox()
        {
            InitializeComponent();
            this.pictureBox.AllowDrop = true;
            this.pictureBox.SizeMode = PictureBoxSizeMode.Zoom;
            this.pictureBox.DragDrop += 
                new DragEventHandler(PictureBox_DragDrop);
            this.pictureBox.DragEnter +=
                 new DragEventHandler(PictureBox_DragEnter);
        }
        public void DisposeImage()
        {
            if (this.pictureBox.Image != null)
            {
                this.pictureBox.Image.Dispose();
                this.pictureBox.Image = null;
            }
        }
        private Image ZoomImage(Image oldImage)
        {
            if (null == oldImage)
            {
                return null;
            }
            int width = oldImage.Width;
            int height = oldImage.Height;
            int newWidth = 0;
            int newHeight = 0;
            float ratioXY = (float)width / (float)height;
            if ((width > this.pictureBox.Width) || 
                 (height > this.pictureBox.Height))
            {
                if (ratioXY >= 1.0)
                {
                    newWidth = this.pictureBox.Width;
                    newHeight = (int)((float)newWidth / ratioXY) + 1;
                }
                else
                {
                    newHeight = this.pictureBox.Height;
                    newWidth = (int)(newHeight * ratioXY);
                }
                Bitmap bitmap = new Bitmap(newWidth,
                     newHeight, oldImage.PixelFormat);
                Graphics g = Graphics.FromImage(bitmap);
                g.Clear(Color.Transparent);
                g.DrawImage(oldImage, 
                    new RectangleF(0, 0, newWidth, newHeight));
                return Image.FromHbitmap(bitmap.GetHbitmap());
            }
            return oldImage;
        }
        #endregion
        #region PictureBox drag and drop events
        private void PictureBox_DragEnter(object sender, DragEventArgs e)
        {
            bool bflag = e.Data.GetDataPresent(DataFormats.FileDrop);
            e.Effect = bflag && this.m_dragDropEnable ? e.Effect =
                DragDropEffects.Copy : DragDropEffects.None;
        }
        private void PictureBox_DragDrop(object sender, DragEventArgs e)
        {
            if (e.Data.GetDataPresent(DataFormats.FileDrop))
            {
                String[] strfileNames =
                    (String[])e.Data.GetData(DataFormats.FileDrop);
                Image dragImage = Image.FromFile(strfileNames[0]);
                if (dragImage != null)
                {
                    this.DisposeImage();
                    this.pictureBox.Image = 
                        m_isOriginalSize ? dragImage : ZoomImage(dragImage);
                }
            }
        }
        #endregion
    }
}
 

ImageEffectMainWindow.cs类,主窗体

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using ImageEffectSample.Classes;
namespace ImageEffectSample.Windows
{
    public partial class ImageEffectMainWindow : Form
    {
        ImageEffectManager effectManager = null;
        public ImageEffectMainWindow()
        {
            InitializeComponent();
            this.effectManager = new ImageEffectManager();
            this.convertedPictureBox.DragDropEnable = false;
        }
        private void CheckBoxOriginalSize_CheckedChanged(object sender, EventArgs e)
        {
            this.originalPictureBox.IsOriginalSize = 
                this.checkBoxOriginalSize.Checked;
        }
        private void Button_Click(object sender, EventArgs e)
        {
            this.txtBoxTime.Text = "";
            Button button = sender as Button;
            if (button != null)
            {
                TimeCounter.Start();
                Image img = this.originalPictureBox.PictrueBoxObject.Image;
                if (img != null)
                {
                    effectManager.OriginalBitmap = new Bitmap(img);
                    switch (button.Name)
                    {
                        case "btnGrayScaleEffect":
                            effectManager.ChangeEffect(ImageEffect.GrayScale);
                            break;
                        case "btnFileEffect":
                            effectManager.ChangeEffect(ImageEffect.Film);
                            break;
                        case "btnReliefEffect":
                            effectManager.ChangeEffect(ImageEffect.Relief);
                            break;
                        case "btnSoftenEffect":
                            effectManager.ChangeEffect(ImageEffect.Soften);
                            break;
                        case "btnSharpenEffect":
                            effectManager.ChangeEffect(ImageEffect.Sharpen);
                            break;
                    }
                    this.convertedPictureBox.DisposeImage();
                    this.convertedPictureBox.PictrueBoxObject.Image = 
                        effectManager.ConvertedBitmap;
                    TimeCounter.Stop();
                    this.txtBoxTime.Text = 
                        String.Format("Elapsed time:  {0}  s",
                            TimeCounter.Seconds.ToString());
                }
            }
        }
        
        private void ClearImagesButton_Click(object sender, EventArgs e)
        {
            this.convertedPictureBox.DisposeImage();
            this.originalPictureBox.DisposeImage();
            this.txtBoxTime.Text = "";
        }
    }
}




评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值