【C#图像处理(1)】彩色图像灰度化处理————(2020.7.19学习笔记)

C#学习笔记 专栏收录该内容
19 篇文章 0 订阅

目录

1.灰度化计算公式
2.图像处理的三种方法
3.实例

灰度化计算公式

所有人眼能感知的颜色都是用红色(Red),绿色(Green),蓝色(Blue)三种颜色衍生出来的,所以红绿蓝被称为三原色,同时我们的彩色图像,也是由红®、绿(G)、蓝(B)三个颜色通道的变化以及它们相互之间的叠加来得到的,于是,要将我们的图片从彩色变成灰色,就必须通过计算公式调整RGB,计算公式如下
Gray = R0.299 + G0.587 + B*0.114

图像处理的三种方法

获得计算公式之后,就必须获取并处理图像的信息,这里图像处理的方法有三种,一是提取像素法,二是内存法,三是指针法

提取像素法

该方法使用的是GDI+中的Bitmap.GetPixel和BItmap.SetPixel方法.为了将位图的颜色设置为灰度或其他颜色,就需要使用GetPixel来读取当前像素的颜色,再计算灰度值,最后使用SetPixel来应用新的颜色。

内存法

该方法就是把图像数据直接复制到内存中,这样就使程序的运行速度大大提搞

指针法

该方法与内存法相似,开始都是通过LockBits方法来获取位图的首地址。但该方法更简洁,直接应用指针对位图进行操作。

实例

三个方法实现的效果相同,但每种方法的效率不同,为了更用直观的体现,就用下面的代码和实际运行效果图来表示
From1.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;

namespace 图像处理实例1
{
    public partial class Form1 : Form
    {
        private string curFileName;//文件路径
        private Bitmap curBitmap;//图像对象

        public Form1()
        {
            InitializeComponent();
            myTimer = new HiPerfTimer();
        }

        private void Open_Click(object sender, EventArgs e)
        {
            OpenFileDialog openDig = new OpenFileDialog();//新建OpenFileDialog(文件打开弹窗)对象
            openDig.Filter = "支持所有图像文件 | *.bmp;*.pcx;*.png;*.jpg;*.gif;"+ "*.tif;*.ico;*.dxf;*.cgm;*.cdr;*.wmf;*.eps;*.emf|"+ "位图(*.bmp;*.jpg;*.png;...) | *.bmp;*.pcx;*.png;*.jpg;*.gif;*.tif;*.ico|"+ "矢量图(*.wmf;*.eps;*.emf;...)|*.dxf;*.cgm;*.cdr;*.wmf;*.eps;*.emf;";//为图像选择一个筛选器
            openDig.Title = "打开图像文件";//设置弹窗标题
            openDig.ShowHelp = true;//启用“帮助”按钮
            if(openDig.ShowDialog()==DialogResult.OK)
            {
                curFileName = openDig.FileName;//读取当前选中文件的路径
                try
                {
                     
             
                curBitmap = (Bitmap)Image.FromFile(curFileName);//将装载的图片显式转化为Bitmap类型
                }
                catch(Exception exp)
                {
                    MessageBox.Show(exp.Message);
                }
                
            }
            Invalidate();
        }

        private void Save_Click(object sender, EventArgs e)
        {
            if(curBitmap==null)
            {
                return;

            }
            SaveFileDialog saveDig = new SaveFileDialog();
            saveDig.Title = "保存为";
            saveDig.OverwritePrompt = true;
            saveDig.Filter = "BMP文件{*.bmp}|*.bmp" + "Gif文件(*.gif)|*.gif|" + "JPEG文件(*.jpg)|*.jpg" + "PNG文件(*.png)|*.png";
            saveDig.ShowHelp = true;

            if(saveDig.ShowDialog()==DialogResult.OK)
            {
                string fileName = saveDig.FileName;
                string strFilExtn = fileName.Remove(0, fileName.Length - 3);//删除保存路径除了最后三个字符的所有字符,即只保留文件的后缀名
                switch(strFilExtn)//根据后缀名,选择保存方式
                {
                    case "bmp":
                        curBitmap.Save(fileName, System.Drawing.Imaging.ImageFormat.Bmp);
                        break;
                    case "jpg":
                        curBitmap.Save(fileName, System.Drawing.Imaging.ImageFormat.Jpeg);
                        break;
                    case "gif":
                        curBitmap.Save(fileName, System.Drawing.Imaging.ImageFormat.Gif);
                        break;
                    case "tif":
                        curBitmap.Save(fileName, System.Drawing.Imaging.ImageFormat.Tiff);
                        break;
                    case "png":
                        curBitmap.Save(fileName, System.Drawing.Imaging.ImageFormat.Png);
                        break;
                    default:
                        break;


                }
            }
        }

        private void Close_Click(object sender, EventArgs e)
        {
            this.Close();

        }

        private void Form1_Paint(object sender, PaintEventArgs e)
        {
            Graphics g = e.Graphics;
            if(curBitmap!=null)
            {
                g.DrawImage(curBitmap, 160, 20, curBitmap.Width, curBitmap.Height);//绘制图像

            }
        }

        private void Pixel_Click(object sender, EventArgs e)
        {
            if(curBitmap!=null)
            {
                myTimer.ClearTimer();
                myTimer.Start();
                Color curColor;
                int ret;
                for(int i=0;i<curBitmap.Width;i++)
                {
                    for(int j=0;j<curBitmap.Height;j++)
                    {
                        curColor = curBitmap.GetPixel(i, j);
                        ret = (int)(curColor.R * 0.299 + curColor.G * 0.587 + curColor.B * 0.114);
                        curBitmap.SetPixel(i, j, Color.FromArgb(ret, ret, ret));

                    }
                }

             
                myTimer.Stop();
                timeBox.Text = myTimer.Duration.ToString("####.##") + "毫秒";
                Invalidate();

            }
        }

        private void Pointer_Click(object sender, EventArgs e)
        {
            if(curBitmap!=null)
            {
                myTimer.ClearTimer();//去除之前的计时数据
                myTimer.Start();//开始计时
                Rectangle rect = new Rectangle(0, 0, curBitmap.Width, curBitmap.Height);//新建一个位置在(0,0)且长宽与导入图片长宽相同的Rectangle对象,当做绘板
                System.Drawing.Imaging.BitmapData bmpData = curBitmap.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadWrite, curBitmap.PixelFormat);//新建BitmapData变量,并将curBitmap锁定到系统内存中并赋值给BitmapData变量,其中ImageLockMode.ReadWrite是指允许对图像进行读写,而参数curBitmap.PixelFormat决定图像信息以像素格式的方式存储
                byte temp = 0;
                unsafe
                {
                    byte* ptr = (byte*)(bmpData.Scan0);//新建指针变量ptr,并将之前bmpData数据的首地址赋予该变量
                    for (int i=0;i<bmpData.Height;i++)
                    {
                        for(int j=0;j<bmpData.Width;j++)
                        {
                            temp = (byte)(0.299 * ptr[2] + 0.587 * ptr[1] + 0.114 * ptr[0]);
                            ptr[0] = ptr[1] = ptr[2] = temp;
                            ptr += 3;//+3指跳过一组rgb三原色字节

                        }
                        ptr += bmpData.Stride - bmpData.Width * 3;/*Stride指实际字节数,
                                                                   * 实际字节数是离图片真实字节数最近的四倍数,所以要跳过这多出来的部分,
                                                                   * 而图片一个像素有R,G,B三个原色字节,所以真实字节数等于bmpData.Width * 3,而多出来的部分就是bmpData.Stride - bmpData.Width * 3 */

                    }
                }
                curBitmap.UnlockBits(bmpData);//解锁
                myTimer.Stop();
                timeBox.Text = myTimer.Duration.ToString("####.##") + " 毫秒";
                Invalidate();


            }
        }

        private void Memory_Click(object sender, EventArgs e)
        {
            if(curBitmap!=null)
            {
                myTimer.ClearTimer();
                myTimer.Start();
                Rectangle rect = new Rectangle(0, 0, curBitmap.Width, curBitmap.Height);//新建矩形绘板
                System.Drawing.Imaging.BitmapData bmpData = curBitmap.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadWrite, curBitmap.PixelFormat);
                IntPtr ptr = bmpData.Scan0;
                int bytes = curBitmap.Width * curBitmap.Height * 3;
                byte[] rgbValues = new byte[bytes];
                System.Runtime.InteropServices.Marshal.Copy(ptr, rgbValues, 0, bytes); //将intPtr变量ptr(非托管对象)复制到一维的字节数组rgbValues(托管对象)中
                double colorTemp = 0;
                for(int i=0;i<rgbValues.Length;i+=3)
                {
                    colorTemp = rgbValues[i + 2] * 0.299 + rgbValues[i + 1] * 0.587 + rgbValues[i] * 0.114;
                    rgbValues[i] = rgbValues[i + 1] = rgbValues[i + 2] = (byte)colorTemp;

                }
                System.Runtime.InteropServices.Marshal.Copy(rgbValues, 0, ptr, bytes);//将一维的字节数组rgbValues(托管对象)复制到intPtr变量ptr(非托管对象)中
                curBitmap.UnlockBits(bmpData);
                myTimer.Stop();
                timeBox.Text = myTimer.Duration.ToString("####.##") + "毫秒";
                Invalidate();

            }
        }
    }
}

HiPerfTimer.cs(自定义计时类)

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;

namespace 图像处理实例1
{
    class HiPerfTimer
    {
        [DllImport("Kernel32.dll")]
        private static extern bool QueryPerformanceCounter(out long lpPerformanceCount);
        [DllImport("Kernel32.dll")]
        private static extern bool QueryPerformanceFrequency(out long lpFrequency);

        private long startTime, stopTime;
        private long freq;

        public HiPerfTimer()
        {
            startTime = 0;
            stopTime = 0;
            if(QueryPerformanceFrequency(out freq)==false)
            {
                throw new Win32Exception();

            }

        }
        public void Start()
        {
            Thread.Sleep(0);
            QueryPerformanceCounter(out startTime);

        }
        public void Stop()
        {
            QueryPerformanceCounter(out stopTime);

        }
        public double Duration
        {
            get
            {
                return (double)(stopTime - startTime) * 1000 / (double)freq;

            }
        }
        public void ClearTimer()
        {
            startTime = 0;
            stopTime = 0;

        }

    }
}

运行效果图如下
在这里插入图片描述
可以看出,提取像素法是最慢的,而指针法是最快的

  • 0
    点赞
  • 0
    评论
  • 2
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

©️2021 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值