为什么用c#进行处理,c#语言相对简单,可以更好的对图像处理算法进行深入理解。进使用c#语言进行图像处理,主要需要了解下面几个类
一、主要用到的类
Bitmap类
详见:https://docs.microsoft.com/zh-cn/dotnet/api/system.drawing.bitmap?view=dotnet-plat-ext-5.0
主要需要关注LockBits、UnlockBits、GetPixel、SetPixel、Clone等方法;Pixelformat的枚举、Lockmode枚举等
BitmapData类
主要需要关注Width、Height、PixelFormat、Scan0、Stride等属性
Graphics类
详见:https://docs.microsoft.com/zh-cn/dotnet/api/system.drawing.graphics?view=dotnet-plat-ext-5.0
Graphics类提供用于将对象绘制到显示设备的方法。 Graphics与特定的设备上下文相关联。
二、彩色图像灰度化
最常用的公式:Gray(i,j)=0.299R(i,j)+0.587×G(i,j)+0.114×B(i,j)
for(int i = 0; i < bitmpap.Width; i++)
{
for(int j = 0; j < bitmpap.Height; j++)
{
//获取该像素点的RGB颜色值
curColor = bitmpap.GetPixel(i, j);
//利用公式计算灰度值
ret = (int)(curColor.R * 0.299 + curColor.G * 0.587 + curColor.B * 0.114);
//设置该像素点的灰度值,R=G=B=ret
bitmpap.SetPixel(i, j, Color.FromArgb(ret, ret, ret));
}
}
三、线形点运算
顾名思义,就是对每个点进行套入统一公式的运算,灰度变换函数: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为负值,暗区域将变亮,亮区域将变暗,该操作完成了图像求补。
Rectangle rect = new Rectangle(0, 0, curBitmpap.Width, curBitmpap.Height);
System.Drawing.Imaging.BitmapData bmpData = curBitmpap.LockBits(rect,
System.Drawing.Imaging.ImageLockMode.ReadWrite, curBitmpap.PixelFormat);
IntPtr ptr = bmpData.Scan0;
int bytes = curBitmpap.Width * curBitmpap.Height;
byte[] grayValues = new byte[bytes];
System.Runtime.InteropServices.Marshal.Copy(ptr, grayValues, 0, bytes);
int temp = 0;
//得到斜率,自定义的
double a = -1;
//得到偏移量,自定义的
double b = 255);
for (int i = 0; i<bytes; i++)
{
//根据公式计算线性点运算
//加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);
curBitmpap.UnlockBits(bmpData);
四、灰度直方图
灰度直方图是灰度的函数,描述的是图像中具有该灰度级的像素的个数。如果用直角坐标系来表示,则它的横坐标是灰度级,纵坐标是该灰度出现的概率(像素的个数)。
计算各个灰度级所具有的像素个数
//图像数据
private System.Drawing.Bitmap bmpHist;
//灰度等级
private int[] countPixel = new int[256];
//记录最大的灰度级个数
private int maxPixel;
private void calchist()
{
//锁定8位灰度位图
Rectangle rect = new Rectangle(0, 0, bmpHist.Width, bmpHist.Height);
System.Drawing.Imaging.BitmapData bmpData = bmpHist.LockBits(rect,
System.Drawing.Imaging.ImageLockMode.ReadWrite, bmpHist.PixelFormat);
IntPtr ptr = bmpData.Scan0;
int bytes = bmpHist.Width * bmpHist.Height;
byte[] grayValues = new byte[bytes];
System.Runtime.InteropServices.Marshal.Copy(ptr, grayValues, 0, bytes);//灰度值数据存入grayValues中
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(bmpData);
}
绘制直方图
private void histForm_Paint(object sender, PaintEventArgs e)
{
//获取Graphics对象
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();
}
五、直方图灰度拉伸
灰度拉伸,也称对比度拉伸,是一种简单的线性点运算。它扩展图像的直方图,使其充满整个灰度等级范围内。
设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)为:
private void calchist()
{
//锁定8位灰度位图
Rectangle rect = new Rectangle(0, 0, bmpHist.Width, bmpHist.Height);
System.Drawing.Imaging.BitmapData bmpData = bmpHist.LockBits(rect,
System.Drawing.Imaging.ImageLockMode.ReadWrite, bmpHist.PixelFormat);
IntPtr ptr = bmpData.Scan0;
int bytes = bmpHist.Width * bmpHist.Height;
byte[] grayValues = new byte[bytes];
System.Runtime.InteropServices.Marshal.Copy(ptr, grayValues, 0, bytes);//灰度值数据存入grayValues中
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++)
{
//加0.5照例是为了四舍五入
grayValues[i] = (byte)(p*(grayValues[i]-a) + 0.5);
}
//解锁
System.Runtime.InteropServices.Marshal.Copy(grayValues, 0, ptr, bytes);
bmpHist.UnlockBits(bmpData);
}