前言:
这篇文章是一篇关于用c#实现直方图均衡化的简单demo。
这个是遥感老师布置的c#作业,由于最近一直在进行一个UWP的项目,大部分c#前台界面都是用xaml写的。但这个demo是一个窗体程序所以界面爆炸丑,望大家见谅。(虽然其他界面也写的丑 ( o=^•ェ•)o ┏━┓)
不多说。。。。。
先附上项目的Git地址:点击打开链接
开整:
首先简单的说一下直方图均衡化的步骤:
1 求原图的总像数个数 ;
int size = width * height;
2 计算各个灰度值对应的像元个数;
for (int i = 0; i < width; ++i) for (int j = 0; j < height; ++j) { Color pixel = newbitmap.GetPixel(i, j); //计算各像元值的个数 gray[Convert.ToInt16(pixel.R)] += 1; //由于是灰度只读取R值 }3 计算各像元值占比;
for (int i = 0; i < 256; i++) { graydense[i] = (gray[i] * 1.0) / size; //每个灰度像素个数占比 }4 计算累计频率占比;
for (int i = 1; i < 256; i++) { graydense[i] = graydense[i]+ graydense[i - 1]; //累计百分比 }5 根据变换公式改变各频率对应的灰度值 公式: 新灰度值 = 累计频率占比 * 255。
newpixel = Convert.ToInt16(graydense[Convert.ToInt16(pixel.R)] * 255);
了解了步骤然后就开始写demo吧!
写个简单的窗体界面:
我的IDE为Visual Studio 2017
先看看demo的界面
然后就是界面里包含的控件
picturebox1和picturebox2控件分别用来显示原始图像和处理后的图像
button1和openfiledialog1是open打开文件按钮
button2和savefiledialog1是保存图片按钮
button3的click事件为均衡化操作
两个label Original Image和New Image
然后再看看三个按钮的后台事件的带码
button1_Click:
private void button1_Click(object sender, EventArgs e) { if (openFileDialog1.ShowDialog() == DialogResult.OK) { string path = openFileDialog1.FileName; bitmap = (Bitmap)Image.FromFile(path); pictureBox1.Image = bitmap.Clone() as Image; } }
button2_Click:
private void button2_Click(object sender, EventArgs e) { bool isSave = true; if (saveFileDialog1.ShowDialog() == DialogResult.OK) { string fileName = saveFileDialog1.FileName.ToString(); if (fileName != "" && fileName != null) { string fileExtName = fileName.Substring(fileName.LastIndexOf(".") + 1).ToString(); System.Drawing.Imaging.ImageFormat imgformat = null; if (fileExtName != "") { switch (fileExtName) { case "jpg": imgformat = System.Drawing.Imaging.ImageFormat.Jpeg; break; case "bmp": imgformat = System.Drawing.Imaging.ImageFormat.Bmp; break; case "gif": imgformat = System.Drawing.Imaging.ImageFormat.Gif; break; default: MessageBox.Show("只能存取为: jpg,bmp,gif 格式"); isSave = false; break; } } //默认保存为JPG格式 if (imgformat == null) { imgformat = System.Drawing.Imaging.ImageFormat.Jpeg; } if (isSave) { try { this.pictureBox2.Image.Save(fileName, imgformat); //MessageBox.Show("图片已经成功保存!"); } catch { MessageBox.Show("保存失败,你还没有截取过图片或已经清空图片!"); } } } } }
button3_Click:
private void button3_Click(object sender, EventArgs e) { if (bitmap != null) { newbitmap = bitmap.Clone() as Bitmap;//clone一个副本 int width = newbitmap.Width; int height = newbitmap.Height; int size = width * height; //总像数个数 int[] gray = new int[256]; //定义一个int数组,用来存放各像元值的个数 double[] graydense = new double[256]; //定义一个float数组,存放每个灰度像素个数占比 for (int i = 0; i < width; ++i) for (int j = 0; j < height; ++j) { Color pixel = newbitmap.GetPixel(i, j); //计算各像元值的个数 gray[Convert.ToInt16(pixel.R)] += 1; //由于是灰度只读取R值 } for (int i = 0; i < 256; i++) { graydense[i] = (gray[i] * 1.0) / size; //每个灰度像素个数占比 } for (int i = 1; i < 256; i++) { graydense[i] = graydense[i]+ graydense[i - 1]; //累计百分比 } for (int i = 0; i < width; ++i) for (int j = 0; j < height; ++j) { Color pixel = newbitmap.GetPixel(i, j); int oldpixel = Convert.ToInt16(pixel.R);//原始灰度 int newpixel = 0; if (oldpixel == 0) newpixel = 0; //如果原始灰度值为0则变换后也为0 else newpixel = Convert.ToInt16(graydense[Convert.ToInt16(pixel.R)] * 255); //如果原始灰度不为0,则执行变换公式为 <新像元灰度 = 原始灰度 * 累计百分比> pixel = Color.FromArgb(newpixel, newpixel, newpixel); newbitmap.SetPixel(i, j, pixel);//读入newbitmap } pictureBox2.Image = newbitmap.Clone() as Image;//显示至pictureBox2 } } } }
处理效果:
最后:
最为关键的均衡化操作我的注释写的贼清楚,大家就自己看。
完整的代码看上面的github链接。
良心代码绝不留坑!复制粘贴既有效!复制粘贴既有效!(。・∀・)ノ
如果复制粘贴无效,你可以。。。。。。。。。。。。。。。顺着网线来砍我啊!