c#图像处理算法学习
直方图匹配
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Text; using System.Windows.Forms; namespace 图像处理DEMO1 { public partial class 直方图匹配 : Form { private string shapingFileName; private System.Drawing.Bitmap shapingBitmap; private int[] shapingPixel; private double[] cumHist; private int shapingSize; private int maxPixel; public double[] ApplicatinP { get { return cumHist; } } public 直方图匹配() { InitializeComponent(); shapingPixel = new int[256]; cumHist=new double[256]; } private void button1_Click(object sender, EventArgs e) { OpenFileDialog openf = new OpenFileDialog(); openf.Filter = "所有图片格式|*.Jpg"; openf.Title = "打开图片"; openf.ShowHelp = true; if (openf.ShowDialog() == DialogResult.OK) { shapingFileName = openf.FileName; try { shapingBitmap = (Bitmap)Image.FromFile(shapingFileName); } catch (Exception exp) { MessageBox.Show(exp.Message); } //计算图像灰度等级像素个数 Rectangle rect = new Rectangle(0,0,shapingBitmap.Width,shapingBitmap.Height); System.Drawing.Imaging.BitmapData jpgData = shapingBitmap.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadWrite, shapingBitmap.PixelFormat); IntPtr ptr = jpgData.Scan0; shapingSize = jpgData.Stride * jpgData.Height; byte[] grayValues = new byte[shapingSize]; System.Runtime.InteropServices.Marshal.Copy(ptr, grayValues, 0, shapingSize); byte temp = 0; maxPixel = 0; Array.Clear(shapingPixel,0,256); for (int i = 0; i < shapingSize; i++) { temp = grayValues[i]; shapingPixel[temp]++; if (shapingPixel[temp] > maxPixel) { maxPixel = shapingPixel[temp]; } } System.Runtime.InteropServices.Marshal.Copy(grayValues,0,ptr,shapingSize); shapingBitmap.UnlockBits(jpgData); } Invalidate(); } private void button2_Click(object sender, EventArgs e) { this.DialogResult = DialogResult.OK; } private void button3_Click(object sender, EventArgs e) { this.Close(); } private void 直方图匹配_Paint(object sender, PaintEventArgs e) { if (shapingBitmap != null) { //在窗体中绘制直方图 Pen curPen = new Pen(Brushes.Black,1); Graphics g = e.Graphics; g.DrawLine(curPen,50,240,320,240); g.DrawLine(curPen,50,240,50,30); g.DrawLine(curPen,100,240,100,242); g.DrawLine(curPen,150,240,150,242); g.DrawLine(curPen,200,240,200,242); g.DrawLine(curPen,250,240,250,242); g.DrawLine(curPen,300,240,300,242); g.DrawString("0",new Font("New Timer",8),Brushes.Black,new PointF(46,242)); g.DrawString("50",new Font ("New Timer",8),Brushes.Black,new PointF (92,242)); g.DrawString("100",new Font ("New Timer",8),Brushes.Black,new PointF(139,242)); g.DrawString("150", new Font("New Timer", 8), Brushes.Black, new PointF(189, 242)); g.DrawString("200", new Font("New Timer", 8), Brushes.Black, new PointF(239, 242)); g.DrawString("250", new Font("New Timer", 8), Brushes.Black, new PointF(289, 242)); g.DrawLine(curPen,48,40,50,40); g.DrawString("0",new Font("New Timer",8),Brushes.Black,new PointF(34,234)); g.DrawString(maxPixel.ToString(),new Font("New Timer",8),Brushes.Black,new PointF (18,34)); double temp = 0; int[] tempArray = new int[256]; for (int i = 0; i < 256; i++) { temp = 200 * (double)shapingPixel[i] / (double)maxPixel; g.DrawLine(curPen,50+i,240,50+i,240-(int)temp); //计算直方图各灰度级的累积分布函数 if (i != 0) { tempArray[i] = tempArray[i - 1] + shapingPixel[i]; } else { tempArray[0] = shapingPixel[0]; } cumHist[i] = (double)tempArray[i] / (double)shapingSize; } curPen.Dispose(); } } } }
灰度直方图
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Text; using System.Windows.Forms; namespace 图像处理DEMO1 { public partial class 灰度直方图 : Form { //灰度直方图:只要遍历二维图片中的所有像素, //计算出每个灰度级的像素个数, //即可得到其灰度直方图 //点运算是一种简单而重要的技术。 /*点运算时只根据 对应像素的输入灰度值来决定该像素输出灰度值的图像处理运算。 * 它有时也被称为对比度增强,对比度拉伸或灰度变换。 * 点运算没有 改变图像内的空间关系,它是按照一定的方式改变了图像的灰度直方图 */ private System.Drawing.Bitmap bmpHist; private int[] countPixel; private int maxPixel; public 灰度直方图(Bitmap bmp) { InitializeComponent(); //把主窗体额图像数据传递给从窗体 bmpHist = bmp; //灰度级计算 countPixel = new int[256]; } /// <summary> /// 绘制直方图 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void 灰度直方图_Paint(object sender, PaintEventArgs e) { //获取Graphic对象 Graphics g = e.Graphics; //创建一个宽度为1的黑色钢笔 Pen curPen = new Pen(Brushes.Black, 1); //绘制坐标轴 g.DrawLine(curPen,50,240,320,240); g.DrawLine(curPen,50,240,50,30); //绘制并标识坐标刻度 g.DrawLine(curPen,100,240,100,242); g.DrawLine(curPen,150,240,150,242); g.DrawLine(curPen,200,240,200,242); g.DrawLine(curPen,250,240,250,242); g.DrawLine(curPen,300,240,300,242); g.DrawString("0", new Font("New Timer", 8), Brushes.Black, new PointF(46 , 242)); g.DrawString("50", new Font("New Timer", 8), Brushes.Black, new PointF(92 , 242)); g.DrawString("100", new Font("New Timer", 8), Brushes.Black, new PointF(139, 242)); g.DrawString("150", new Font("New Timer", 8), Brushes.Black, new PointF(189, 242)); g.DrawString("200", new Font("New Timer", 8), Brushes.Black, new PointF(239, 242)); g.DrawString("250", new Font("New Timer", 8), Brushes.Black, new PointF(289, 242)); g.DrawLine(curPen,48,40,50,40); g.DrawString("0",new Font("New Timer",8),Brushes.Black,new PointF(34,234)); g.DrawString(maxPixel.ToString(),new Font("New Timer",8),Brushes.Black,new PointF(18,34)); //绘制直方图 double temp = 0; for (int i = 0; i < 256; i++) { //纵坐标长度 temp = 200.0 * countPixel[i] / maxPixel; g.DrawLine(curPen, 50 + i, 240, 50 + i, 240 - (int)temp); } //释放对象 curPen.Dispose(); } private void 灰度直方图_Load(object sender, EventArgs e) { //锁定8位灰度位图 Rectangle rect = new Rectangle(0,0,bmpHist.Width,bmpHist.Height); System.Drawing.Imaging.BitmapData jpgData=bmpHist.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadWrite, bmpHist.PixelFormat); IntPtr ptr = jpgData.Scan0; int bytes = bmpHist.Width * bmpHist.Height; byte[] grayValues=new byte[bytes]; System.Runtime.InteropServices.Marshal.Copy(ptr,grayValues,0,bytes); byte temp = 0; maxPixel = 0; //灰度等级数组清零; Array.Clear(countPixel,0,256); //计算各个灰度级的像素个数 for (int i = 0; i < bytes; i++) { //灰度级 temp = grayValues[i]; //计数加1 countPixel[temp]++; if (countPixel[temp] > maxPixel) { //找到灰度频率最大的下像素数,用于绘制直方图 maxPixel = countPixel[temp]; } } //解锁 System.Runtime.InteropServices.Marshal.Copy(grayValues,0,ptr,bytes); bmpHist.UnlockBits(jpgData); } } }
图像平移
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Text; using System.Windows.Forms; namespace 图像处理DEMO1 { public partial class 图像平移 : Form { public 图像平移() { InitializeComponent(); } private void button1_Click(object sender, EventArgs e) { this.DialogResult = DialogResult.OK; } private void button2_Click(object sender, EventArgs e) { this.Close(); } /// <summary> /// 横向偏移量 /// </summary> public string GetxOffset { get { return xOffset.Text; } } /// <summary> /// 纵向偏移量 /// </summary> public string GetyOffset { get { return yOffset.Text; } } } }
线性点运算
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Text; using System.Windows.Forms; namespace 图像处理DEMO1 { public partial class 线性点运算 : Form { /*灰度图像的点运算可分为 线性点运算 和 非线性点运算 * 定义: * 线性点运算就是输出灰度级与输入灰度级呈线性关系的点运算。 * 灰度变换函数的形式为: * g(x,y)=pf(x,y)+L * 其中, * f(x,y)为输入图像在点(x,y)的灰度值 * g(x,y)为相应的输出点的灰度值。 * p=1,L=0时 g(x,y)=f(x,y) * p<1,输出图像的对比度将变大 * P>1,对比度将减少 * 若P=1而L!=0,该操作仅使所有像素的灰度值上移或下移, * 其效果是使整个图像在显示时更暗或更亮;如果P为负值 * 暗区域将变亮,亮区域将变暗,该操作完成了图像求补 */ public 线性点运算() { InitializeComponent(); } private void button1_Click(object sender, EventArgs e) { //设置DialogResult属性 this.DialogResult = DialogResult.OK; } private void button2_Click(object sender, EventArgs e) { this.Close(); } public string GetScaling { get { //得到斜率 return scaling.Text; } } public string GetOffset { get { //得到偏移量 return offset.Text; } } } }
/// <summary> /// 将图像转化成相同大小,转化成256 X 256。 /// </summary> /// <param name="imageFile">原始图片的完整路径</param> /// <param name="newImageFile">把它保存到了本地了</param> /// <returns></returns> public Bitmap Resize(string imageFile, string newImageFile) { img = Image.FromFile(imageFile); Bitmap imgOutput = new Bitmap(img, 256, 256); imgOutput.Save(newImageFile, System.Drawing.Imaging.ImageFormat.Jpeg); imgOutput.Dispose(); return (Bitmap)Image.FromFile(newImageFile); }
View Code
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Text; using System.Windows.Forms; namespace 图像处理DEMO1 { public partial class Form1 : Form { public Form1() { InitializeComponent(); } Bitmap curBitmap = null; string curFilename; #region 第一章 /// <summary> /// 打开图片 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void button1_Click(object sender, EventArgs e) { OpenFileDialog openf = new OpenFileDialog(); openf.Filter = "所有图片格式|*.Jpg"; openf.Title = "打开图片"; openf.ShowHelp = true; if (openf.ShowDialog() == DialogResult.OK) { curFilename = openf.FileName; curBitmap = (Bitmap)Image.FromFile(curFilename); pictureBox1.Image = (Image)curBitmap; //pictureBox1.Visible = false; } //Invalidate(); 将引发Form_Paint事件 } /// <summary> /// 保存图片 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void button2_Click(object sender, EventArgs e) { if (curBitmap == null) return; else { SaveFileDialog saveF = new SaveFileDialog(); saveF.Title = "另存为:"; saveF.Filter = "图片文件|*.Jpg"; saveF.ShowHelp = true; if (saveF.ShowDialog() == DialogResult.OK) { string filename = saveF.FileName; string strFileExtn = filename.Remove(0,filename.Length-3); switch (strFileExtn) { case "jpg": curBitmap.Save(filename, System.Drawing.Imaging.ImageFormat.Jpeg); break; default: break; } } } } private void Form1_Paint(object sender, PaintEventArgs e) { //Graphics g = e.Graphics; //if (curBitmap != null) //{ // g.DrawImage(curBitmap,0,0,curBitmap.Width,curBitmap.Height); //} } private void Form1_Load(object sender, EventArgs e) { } #endregion #region 第二章 /// <summary> /// 像素提取法:该方法是用的是GDI+中的Bitmap.GetPixel和Bitmap /// SetPixel方法,为了将位图的颜色设置为灰度或其他颜色,就需要使用GetPixel /// 来读取当前像素的颜色,再计算灰度值,最后使用 /// setpixel方法来应用新的颜色。 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void button3_Click(object sender, EventArgs e) { if (curBitmap != null) { Color curColor; int ret; //二维图像数组循环 for (int i = 0; i < curBitmap.Width; i++) { for (int j = 0; j < curBitmap.Height; j++) { //获取该点像素的RGB颜色值 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)); } } } pictureBox1.Image = (Image)curBitmap; } /// <summary> /// 内存法:将程序直接拷贝到系统内存中,运行效率大大提升; /// 未考虑被锁定位图数组是否有未用空间。如果是24位彩色图像此段代码无效 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void button4_Click(object sender, EventArgs e) { if (curBitmap != null) { //位图矩形 Rectangle rect = new Rectangle(0, 0, curBitmap.Width, curBitmap.Height) ; //以可读的方式锁定全部位图像素 System.Drawing.Imaging.BitmapData jpgData = curBitmap.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadOnly, curBitmap.PixelFormat); //得到首地址 IntPtr ptr = jpgData.Scan0; //24位Jpg位图字节数 int bytes = curBitmap.Width * curBitmap.Height * 3; //定义位图数组 byte[] rgbValues=new byte[bytes]; //赋值被锁定的位图像素值到该数组内 System.Runtime.InteropServices.Marshal.Copy(ptr, rgbValues, 0, bytes); //灰度化 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; //R=G=B rgbValues[i] = rgbValues[i + 1] = rgbValues[i + 2] = (byte)colorTemp; } //把数组赋值回位图 System.Runtime.InteropServices.Marshal.Copy(rgbValues,0,ptr,bytes); //解锁位图像素 curBitmap.UnlockBits(jpgData); pictureBox1.Image = (Image)curBitmap; } } /// <summary> /// 内存法:将程序直接拷贝到系统内存中,运行效率大大提升; /// 如果是24位彩色图像使用此段代码.它为了考虑锁定数组中未用的空间而用到了stride属性。 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void button5_Click(object sender, EventArgs e) { if (curBitmap != null) { //位图矩形 Rectangle rect = new Rectangle(0, 0, curBitmap.Width, curBitmap.Height); //以可读写的方式锁定全部位图像素 System.Drawing.Imaging.BitmapData jpgData = curBitmap.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadWrite, curBitmap.PixelFormat); //得到首地址 IntPtr ptr = jpgData.Scan0; //定义被锁定的数组大小,由位图数据与未用控件组成 int bytes = jpgData.Stride * jpgData.Height; byte[] rgbValues = new byte[bytes]; //复制被锁定的位图像素值到该数组 System.Runtime.InteropServices.Marshal.Copy(ptr, rgbValues, 0, bytes); //灰度化 double colorTemp = 0; for (int i = 0; i < jpgData.Height; i++) { //只处理每行中是图像像素的数据,舍弃未用控件 for (int j = 0; j < jpgData.Width*3; j+=3) { colorTemp = rgbValues[i * jpgData.Stride + j + 2] * 0.299 + rgbValues[i * jpgData.Stride + j + 1] * 0.587 + rgbValues[i * jpgData.Stride + j] * 0.114; rgbValues[i * jpgData.Stride + j] = rgbValues[i * jpgData.Stride + j + 1] = rgbValues[i * jpgData.Stride + j + 2] = (byte)colorTemp; } } //把数组复制回位图 System.Runtime.InteropServices.Marshal.Copy(rgbValues,0,ptr,bytes); //解锁位图像素 curBitmap.UnlockBits(jpgData); //显示位图 pictureBox1.Image = (Image)curBitmap; } } /// <summary> /// 指针法:该方法与内存法相似,开始都是通过LockBites方法来获取位图的首地址。 /// 但该方法更简洁,直接应用指针对位图进行操作; /// 为了保持类型安全,在默认情况下c#是不支持指针运算的。因为使用指针会带来相关的风险。所以c# /// 只允许在特别标记的代码块中使用指针。通过unsafe关键字,可以定义 /// 可使用指针的不安全上下文。 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void button6_Click(object sender, EventArgs e) { //位图矩形 Rectangle rect = new Rectangle(0,0,curBitmap.Width,curBitmap.Height); //以可读写的方式锁定全部位图像素; System.Drawing.Imaging.BitmapData jpgData = curBitmap.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadWrite, curBitmap.PixelFormat); byte temp = 0; //启动不安全模式 unsafe { //得到首地址 byte* ptr = (byte*)(jpgData.Scan0); //二维图像环 for (int i = 0; i < jpgData.Height; i++) { for (int j = 0; j < jpgData.Width; j++) { //利用公式计算灰度值 temp = (byte)(0.299 * ptr[2] + 0.587 * ptr[1] + 0.114 * ptr[0]); //R=G=B; ptr[0] = ptr[1] = ptr[2] = temp; //指向下一个像素 ptr += 3; } } //指向下一行数组的首个字节 ptr += jpgData.Stride - jpgData.Width * 3; } //解锁位图像素; curBitmap.UnlockBits(jpgData); //对图片进行显示 pictureBox1.Image = (Image)curBitmap; } private void button7_Click(object sender, EventArgs e) { 灰度直方图 f2 = new 灰度直方图(curBitmap); f2.ShowDialog(); } #endregion /// <summary> /// 线性点运算:设置分辨率 /// 灰度图像的点运算可分为 线性点运算 和 非线性点运算 /// 定义: /// 线性点运算就是输出灰度级与输入灰度级呈线性关系的点运算。 /// 灰度变换函数的形式为: /// g(x,y)=pf(x,y)+L /// 其中, /// f(x,y)为输入图像在点(x,y)的灰度值 /// g(x,y)为相应的输出点的灰度值。 /// p=1,L=0时 g(x,y)=f(x,y) /// p<1,输出图像的对比度将变大 /// P>1,对比度将减少 ///若P=1而L!=0,该操作仅使所有像素的灰度值上移或下移, ///其效果是使整个图像在显示时更暗或更亮;如果P为负值 ///暗区域将变亮,亮区域将变暗,该操作完成了图像求补 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void button8_Click(object sender, EventArgs e) { if (curBitmap != null) { //实例化窗体 线性点运算 f3 = new 线性点运算(); //单机确定按钮 if (f3.ShowDialog() == DialogResult.OK) { Rectangle rect = new Rectangle(0,0,curBitmap.Width,curBitmap.Height); System.Drawing.Imaging.BitmapData jpgData = curBitmap.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadWrite, curBitmap.PixelFormat); IntPtr ptr = jpgData.Scan0; //int bytes = curBitmap.Width * curBitmap.Height; int bytes = jpgData.Stride * jpgData.Height; byte[] grayValues = new byte[bytes]; System.Runtime.InteropServices.Marshal.Copy(ptr,grayValues,0,bytes); int temp = 0; //string类型转换为double类型 //得到斜率 double a = Convert.ToDouble(f3.GetScaling); //得到偏移量 double b = Convert.ToDouble(f3.GetOffset); for (int i = 0; i < bytes; i++) { //根据公式g(x,y)=pf(x,y)+L 计算线性点运算 //加0.5表示四舍五入 temp = (int)(a * grayValues[i] + b + 0.5); //灰度值限制在0-255之间 //大于255则为255;小于0则为0 if (temp > 255) grayValues[i] = 255; else if (temp < 0) grayValues[i] = 0; else grayValues[i] = (byte)temp; } System.Runtime.InteropServices.Marshal.Copy(grayValues,0,ptr,bytes); curBitmap.UnlockBits(jpgData); } pictureBox1.Image = (Image)curBitmap; } } /// <summary> /// 灰度拉伸:如果衣服图像的灰度值分布在全等级灰度范围内,即在0-255之间 /// 那么他更容易被区别确认出来。 /// 灰度拉伸,也称对比度,是一种简单的线性点运算。它扩展图像的直方图, /// 使其冲满整个灰度等级范围内。 /// 设f(x,y)为输入图像,它的最小灰度级A和最大灰度级B的定义为: /// A=min[f(x,y)] B=max[f(x,y)] /// 我们的目标是g(x,y)=pf(x,y)+L,把A和B分别线性映射到0-255,因此,最终的图片 /// g(x,y)=(255/(b-a))*[f(x,y)-A] /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void button9_Click(object sender, EventArgs e) { if (curBitmap != null) { Rectangle rect = new Rectangle(0,0,curBitmap.Width,curBitmap.Height); System.Drawing.Imaging.BitmapData jpgData = curBitmap.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadWrite, curBitmap.PixelFormat); IntPtr ptr = jpgData.Scan0; int bytes = jpgData.Stride * jpgData.Height; byte[] grayValues = new byte[bytes]; System.Runtime.InteropServices.Marshal.Copy(ptr,grayValues, 0, bytes); byte a = 255, b = 0; double p; //计算最大和最小灰度级 for (int i = 0; i < bytes; i++) { //最小灰度级 if (a > grayValues[i]) { a = grayValues[i]; } //最大灰度级 if (b < grayValues[i]) { b= grayValues[i] ; } } //得到倾斜率 p = 255.0 / (b - a); //灰度拉伸 for (int i=0; i < bytes; i++) { //公式 grayValues[i] = (byte)(p * (grayValues[i] - a) + 0.5); } System.Runtime.InteropServices.Marshal.Copy(grayValues, 0, ptr, bytes); curBitmap.UnlockBits(jpgData); pictureBox1.Image = (Image)curBitmap; } } /// <summary> /// 直方图均衡化,又称直方图修平,它是一种很重要的非线性点运算。 /// 该方法通常用来增加图像的局部对比度。尤其是当图片的有用数据的对比度相当接近时候。通过这种方法, /// 亮度可以更好的在直方图上分布; /// 直方图均衡化的基础思想是把原始图像的直方图变换为均匀分布的形式,这样就增加了 /// 像素灰度值的动态范围,从而可达到增加图像整体对比度的效果。 /// 它的具体算法为:首先计算出图像f的各灰度中像素出现的概率。 /// p(i)=ni/n i属于0,1,2,3,...L-1 /// 其中ni表示灰度级i出现的次数,L是图像中所有的灰度数。p实际上市图像的直方图归一化到 /// 0-1范围内。把c作为对应于p的累积概率函数, /// Ci=P(X0)+P(X1)+P(X2)+...+P(Xi) /// C是图像的累积归一化直方图。 /// 创建一个形式为y=T(x)的变化,对于原始图像中的每一个值它就产生一个y,这样y的累积概率 /// 函数就可以再所有值范围内进行线性化,转换公式定义为: /// Yi=T(Xi)=C(i) /// 这时的T是将不同的等级映射到0-1范围内 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void button10_Click(object sender, EventArgs e) { if (curBitmap != null) { Rectangle rect = new Rectangle(0,0,curBitmap.Width,curBitmap.Height); System.Drawing.Imaging.BitmapData jpgData = curBitmap.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadWrite, curBitmap.PixelFormat); IntPtr ptr = jpgData.Scan0; int bytes = jpgData.Stride * jpgData.Height; byte[] grayValues = new byte[bytes]; System.Runtime.InteropServices.Marshal.Copy(ptr, grayValues, 0, bytes); byte temp; int[] tempArray = new int[256]; int[] countPixel = new int[256]; byte[] pixelMap = new byte[256]; //计算各灰度级的像素个数 for (int i = 0; i < bytes;i++ ) { //灰度级 temp = grayValues[i]; //计数加1 countPixel[temp]++; } //计算各度级的累积分布函数 for (int i = 0; i < 256; i++) { if (i != 0) { tempArray[i] = tempArray[i - 1] + countPixel[i]; } else { tempArray[0] = countPixel[0]; } //计算累积概率函数,并把值扩展为0-255范围内 pixelMap[i] = (byte)(255.0 * tempArray[i] / bytes + 0.5); } //灰度等级映射转换处理 for (int i = 0; i < bytes; i++) { temp = grayValues[i]; grayValues[i] = pixelMap[temp]; } System.Runtime.InteropServices.Marshal.Copy(grayValues,0,ptr,bytes); curBitmap.UnlockBits(jpgData); pictureBox1.Image = (Image)curBitmap; } } /// <summary> /// 直方图匹配:又称直方图规定化,是 变换 原图像的 直方图 为规定的 某种形式的直方图 /// 能够指定 想要的 处理后的 图像 的直方图形状 在某些应用中是非常有用的,它属于非线性点运算, /// 直方图均衡化实际上 是 直方图匹配的一个特例。 /// 把现有的直方图为H↓a(a↓k)的图像a(x,y)变换到具有某一制定直方图 /// H↓c(c↓k)的图像c(x,y),一般分两步进行,:先把图像a(x,y)变换为具体均衡化直方图的中间图像 /// b(x,y),而后把b(x,y)变换到c(x,y)。 /// 其方法过程可以概括如下: /// (1)假定a↓k和b↓k的取值范围相同,则分别对H↓a(a↓k)和H↓c(c↓k)作均衡化处理,使a↓k映射成 /// g↓m,c↓k映射成y↓n。g↓m和y↓n的直方图应该是金丝均匀分布的。这时,查找g↓m和y↓n的对应关系,在 /// g↓m约等于y↓n的位置上,找到分别对应于g↓m和y↓n的原来灰度级a↓k和c↓k,于是把此a↓k映射成此c↓k /// 即a↓k --> c↓k. /// (2)把两次映射组合一个函数,使得可由a↓k直接映射成c↓k,若令 /// g↓m=T(a↓k),y↓n=G(c↓k) /// 式中的T()和G()分别是a↓k -->g↓m和c↓k-->y↓n的变换函数,则在g↓m约等于y↓n处,有 /// c↓k=G^-1(y↓n)=G^-1(g↓m)=G^-1[T(a↓k)] /// 式中G^-1是c↓k-->y↓n的反变换函数。由此便得到映射a↓k-->c↓k及其H↓c(c↓i) /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void button11_Click(object sender, EventArgs e) { //实例化“直方图匹配”窗体 直方图匹配 f1 = new 直方图匹配(); if (f1.ShowDialog() == DialogResult.OK) { Rectangle rect = new Rectangle(0, 0, curBitmap.Width, curBitmap.Height); System.Drawing.Imaging.BitmapData jpgData = curBitmap.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadWrite, curBitmap.PixelFormat); IntPtr ptr = jpgData.Scan0; int bytes = jpgData.Stride * jpgData.Height; byte[] grayValues = new byte[bytes]; System.Runtime.InteropServices.Marshal.Copy(ptr, grayValues, 0, bytes); byte temp = 0; double[] PPixel = new double[256]; double[] QPixel = new double[256]; int[] qPixel = new int[256]; int[] tempArray = new int[256]; //计算原图像的灰度级像素个数 for (int i = 0; i < grayValues.Length;i++ ) { temp = grayValues[i]; qPixel[temp]++; } //计算该灰度级的累积分布函数 for (int i = 0; i < 256; i++) { if (i != 0) { tempArray[i] = tempArray[i - 1] + qPixel[i]; } else { tempArray[0] = qPixel[0]; } QPixel[i] = (double)tempArray[i] / (double)bytes; } //得到匹配的直方图的累积分布函数 PPixel = f1.ApplicatinP; double diffA, diffB; byte k = 0; byte[] mapPixel = new byte[256]; //直方图匹配 for (int i = 0; i < 256; i++) { diffB = 1; for (int j = k; j < 256; j++) { //找到两个累积分布函数中最相似的位置 diffA = Math.Abs(QPixel[i] - PPixel[j]); if (diffA - diffB < 1.0E-08) { //记录下差值 diffB = diffA; k = (byte)j; } else { //找到了,几下来位置,并退出,内层循环 k = (byte)(j-1); break; } } //达到最大灰度级,标识未处理灰度级,并推出外循环 if (k == 255) { for (int l = i; l < 256; l++) { mapPixel[l] = k; } break; } //得到映射关系 mapPixel[i] = k; } //灰度级映射处理 for (int i = 0; i < bytes; i++) { temp = grayValues[i]; grayValues[i] = mapPixel[temp]; } System.Runtime.InteropServices.Marshal.Copy(grayValues, 0, ptr, bytes); curBitmap.UnlockBits(jpgData); } this.pictureBox1.Image = (Image)curBitmap; } /// <summary> /// 图像平移定义:图像平移就是使图像沿水平方向和垂直方向移动; /// 具体的算法为:如果把坐标原点(0.0)平移到点(x0,y0)处,则变换公式为 /// x~=x+x↓0 y~=y+y↓0 /// (x,y)为原图像坐标,(x~,y~)为变换后的图像坐标。而图像中的各像素点移动了 /// sqrt(x~2↓0+y~2↓0)距离,上式用矩阵形式表示为 /// /// /// x~ 1 0 x↓0 x /// y~ = 0 1 y↓0 y /// 1 0 0 1 1 /// /// /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void button12_Click(object sender, EventArgs e) { if (curBitmap != null) { //实例化translation窗体 图像平移 f1 = new 图像平移(); if (f1.ShowDialog() == DialogResult.OK) { Rectangle rect = new Rectangle(0,0,curBitmap.Width,curBitmap.Height); System.Drawing.Imaging.BitmapData jpgData = curBitmap.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadWrite, curBitmap.PixelFormat); IntPtr ptr = jpgData.Scan0; int bytes = jpgData.Stride * jpgData.Height; byte[] grayValues = new byte[bytes]; System.Runtime.InteropServices.Marshal.Copy(ptr,grayValues,0,bytes); //得到两个方向的图像平移量 int x = Convert.ToInt32(f1.GetxOffset); int y = Convert.ToInt32(f1.GetyOffset); byte[] tempArray = new byte[bytes]; //临时数组初始化为白色(255)像素 for (int i = 0; i < bytes; i++) { tempArray[i] = 255; } //平移运算 for (int i = 0; i < curBitmap.Height; i++) { //保证纵向平移不出界 if ((i + y) < curBitmap.Height && (i + y) > 0) { for (int j = 0; j < curBitmap.Width; j++) { //保证横向平移不出界 if ((j + x) < curBitmap.Width && (j + x) > 0) { //应用公式 tempArray[(j + x) + (i + y) * curBitmap.Width] = grayValues[j + i * curBitmap.Width]; } } } } //数组复制,返回平移后的图像 grayValues=(byte[])tempArray.Clone(); System.Runtime.InteropServices.Marshal.Copy(grayValues,0,ptr,bytes); curBitmap.UnlockBits(jpgData); } pictureBox1.Image = (Image)curBitmap; } } } }