x01.BitmapHelper:图像处理
“所有致我于死地的,也激发我胆魄”,姚贝娜的《心火》,是我近年来听过最好的歌,特此推荐一下。
图像处理,大概分三步:1.LockBits();2.进行处理;3.UnlockBits();这比起 C++ 来,不知清爽几许?
编程,是为了满足人的需求,所以进行灰度处理时,不是简单的 (r + g + b) / 3,而是分别乘以合适的比例值,r * 0.30 + g * 0.59 + b * 0.11。这是因为人眼对 green 最敏感,red 次之,blue 最低。
只实现了灰度处理,边缘提取,二值化,缩小,Rotate 等有限功能,代码不言自明,无需多说。
using System; using System.Collections.Generic; using System.Drawing; using System.Drawing.Imaging; using System.Linq; using System.Runtime.InteropServices; using System.Text; namespace x01.Utilities { public static class BitmapHelper { /// <summary> /// 对 bitmap 进行灰度处理。r,g,b 权重相加为 1。 /// </summary> /// <param name="bmp">所要处理的 bitmap 对象。</param> /// <param name="rWeight">red 权重。</param> /// <param name="gWeight">green 权重。</param> /// <param name="bWeight">blue 权重。</param> public static void Gray(Bitmap bmp, float rWeight = 0.30f, float gWeight = 0.59f, float bWeight = 0.11f) { BitmapData data = bmp.LockBits(new Rectangle(new Point(), bmp.Size), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb); byte[] buf = new byte[data.Stride * bmp.Height]; Marshal.Copy(data.Scan0, buf, 0, buf.Length); for (int y = 0; y < bmp.Height * data.Stride; y += data.Stride) { for (int x = 0; x < bmp.Width * 3; x += 3) { int i = y + x; byte b = buf[i]; byte g = buf[i + 1]; byte r = buf[i + 2]; buf[i] = buf[i + 1] = buf[i + 2] = (byte)(b * bWeight + g * gWeight + r * rWeight); } } Marshal.Copy(buf, 0, data.Scan0, buf.Length); bmp.UnlockBits(data); } /// <summary> /// 对 bitmap 进行中值滤波处理。 /// </summary> /// <param name="bmp">所要处理的 bitmap 对象。</param> public static void MedianFilter(Bitmap bmp) { BitmapData data = bmp.LockBits(new Rectangle(new Point(), bmp.Size), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb); unsafe { byte* pBuf = (byte*)data.Scan0.ToPointer(); for (int i = 1; i < bmp.Height - 1; i++) { for (int j = 1; j < bmp.Width - 1; j++) { List<byte>[] list = new List<byte>[3]; for (int k = 0; k < 3; k++) { list[k] = new List<byte>(); } for (int y = -1; y < 2; y++) { for (int x = -1; x < 2; x++) { int index = (i + y) * data.Stride + (j + x) * 3; for (int k = 0; k < 3; k++) { list[k].Add(pBuf[index + k]); } } } for (int k = 0; k < 3; k++) { list[k].Sort(); } int indexMedian = i * data.Stride + j * 3; for (int k = 0; k < 3; k++) { pBuf[indexMedian + k] = list[k][4]; // 4: median value after sort. list[k].Clear(); } list = null; } } } bmp.UnlockBits(data); } /// <summary> /// 获取 bitmap 所选的颜色分量直方图。 /// </summary> /// <param name="bmp">所要处理的 bitmap 对象</param> /// <param name="bgrOffset">所选的颜色: blue=0, green=1, red=2</param> /// <returns>返回 256 * 256 大小的 Bitmap 直方图。</returns> public static Bitmap Histogram(Bitmap bmp, int bgrOffset = 0) { BitmapData data = bmp.LockBits(new Rectangle(new Point(), bmp.Size), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb); byte[] buf = new byte[data.Stride * bmp.Height]; Marshal.Copy(data.Scan0, buf, 0, buf.Length); int[] countColors = new int[256]; for (int y = 0; y < bmp.Height; y++) { for (int x = 0; x < bmp.Width; x++) { int index = y * data.Stride + x * 3; countColors[buf[index + bgrOffset]]++; } } bmp.UnlockBits(data); int maxIndex = 0; for (int i = 1; i < 256; i++) { if (countColors[maxIndex] < countColors[i]) { maxIndex = i; } } Bitmap hist = new Bitmap(256, 256); Graphics g = Graphics.FromImage(hist); g.Clear(Color.Wheat); for (int i = 0; i < 256; i++) { int h = 256 * countColors[i] / countColors[maxIndex]; g.DrawLine(new Pen(Color.FromArgb(i, i, i), 1f), i, 256, i, 256 - h); // top bottom inverse } return hist; } public static void ExtractEdge(Bitmap bmp) { BitmapData data = bmp.LockBits(new Rectangle(new Point(), bmp.Size), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb); byte[] buf = new byte[data.Stride * bmp.Height]; byte[] edge = new byte[data.Stride * bmp.Height]; Marshal.Copy(data.Scan0, buf, 0, buf.Length); for (int y = 1; y < bmp.Height - 1; y++) { for (int x = 1; x < bmp.Width - 1; x++) { int index = y * data.Stride + x * 3; byte gray = (byte)((buf[index] + buf[index + 1] + buf[index + 2]) / 3); int value = 0; for (int i = -1; i < 2; i++) // neareat eight point { for (int j = -1; j < 2; j++) { if (i == 0 && j == 0) { continue; } int index2 = (y + i) * data.Stride + (x + i) * 3; byte gray2 = (byte)((buf[index2] + buf[index2 + 1] + buf[index2 + 2]) / 3); value += Math.Abs(gray - gray2); } } edge[index] = edge[index + 1] = edge[index + 2] = (byte)(value >> 3); } } Marshal.Copy(edge, 0, data.Scan0, edge.Length); bmp.UnlockBits(data); } /// <summary> /// 对图像进行二值化处理。 /// </summary> /// <param name="bmp">处理的图像。</param> /// <param name="minValue">最小阀值。</param> /// <param name="maxValue">最大阀值。</param> public static void Binary(Bitmap bmp, int minValue, int maxValue) { BitmapData data = bmp.LockBits(new Rectangle(new Point(), bmp.Size), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb); byte[] buf = new byte[data.Stride * bmp.Height]; Marshal.Copy(data.Scan0, buf, 0, buf.Length); for (int y = 0; y < bmp.Height; y++) { for (int x = 0; x < bmp.Width; x++) { int index = y * data.Stride + x * 3; int gray = (buf[index] + buf[index+1] +buf[index+2])/3; if (gray >= minValue && gray <= maxValue) { buf[index] = buf[index + 1] = buf[index + 2] = 0; } else { buf[index] = buf[index + 1] = buf[index + 2] = 255; } } } Marshal.Copy(buf, 0, data.Scan0, buf.Length); bmp.UnlockBits(data); } /// <summary> /// 对灰度图像进行对比度增强。 /// </summary> /// <param name="bmp">灰度图像</param> /// <param name="srcMin">原灰度下界</param> /// <param name="srcMax">原灰度上界</param> /// <param name="destMin">目标灰度下界</param> /// <param name="destMax">目标灰度上界</param> public static void Enhance(Bitmap bmp, int srcMin, int srcMax, int destMin, int destMax) { BitmapData data = bmp.LockBits(new Rectangle(new Point(), bmp.Size), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb); byte[] buf = new byte[data.Stride * bmp.Height]; Marshal.Copy(data.Scan0, buf, 0, buf.Length); for (int y = 0; y < bmp.Height; y++) { for (int x = 0; x < bmp.Width; x++) { int index = y * data.Stride + x * 3; int gray = (buf[index] + buf[index + 1] + buf[index + 2]) / 3; float value = (float)(destMax - destMin) / (srcMax - srcMin) * (gray - srcMin) + destMin; buf[index] = buf[index + 1] = buf[index + 2] = value < byte.MinValue ? byte.MinValue : (value > byte.MaxValue ? byte.MaxValue : (byte)value); } } Marshal.Copy(buf, 0, data.Scan0, buf.Length); bmp.UnlockBits(data); } public static void Enhance(Bitmap bmp, float n) { BitmapData data = bmp.LockBits(new Rectangle(new Point(), bmp.Size), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb); byte[] buf = new byte[data.Stride * bmp.Height]; Marshal.Copy(data.Scan0, buf, 0, buf.Length); for (int y = 0; y < bmp.Height; y++) { for (int x = 0; x < bmp.Width; x++) { int index = y * data.Stride + x * 3; int gray = (buf[index] + buf[index + 1] + buf[index + 2]) / 3; float value = gray * n; buf[index] = buf[index + 1] = buf[index + 2] = value < byte.MinValue ? byte.MinValue : (value > byte.MaxValue ? byte.MaxValue : (byte)value); } } Marshal.Copy(buf, 0, data.Scan0, buf.Length); bmp.UnlockBits(data); } public static void ConnectRegion(Bitmap bmp) { BitmapData data = bmp.LockBits(new Rectangle(new Point(), bmp.Size), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb); byte[] buf = new byte[data.Stride * bmp.Height]; Marshal.Copy(data.Scan0, buf, 0, buf.Length); int sign = 40; for (int y = 0; y < bmp.Height; y++) { for (int x = 0; x < bmp.Width; x++) { int index = y * data.Stride + x * 3; if (buf[index] == 0) { SignRegion(buf, x * 3, y * data.Stride, sign, data.Stride, bmp.Width, bmp.Height); sign += 40; } } } Marshal.Copy(buf, 0, data.Scan0, buf.Length); bmp.UnlockBits(data); } public static void SignRegion(byte[] buf, int x, int y, int sign, int stride, int width, int height) { int index = x + y; buf[index] = buf[index + 1] = buf[index + 2] = (byte)sign; if (x > 0 && buf[index -3] == 0) // left { SignRegion(buf, x - 3, y, sign, stride, width, height); } if (x < width * 3 - 3 && buf[index + 3] == 0) // right { SignRegion(buf, x + 3, y, sign, stride, width, height); } if (y > 0 && buf[index - stride] == 0) // top { SignRegion(buf, x, y - stride, sign, stride, width, height); } if (y < height * stride && buf[index + stride] == 0) // bottom { SignRegion(buf, x, y + stride, sign, stride, width, height); } } /// <summary> /// 缩小图像。 /// </summary> /// <param name="bmp">图像</param> /// <param name="widthFactor">宽度缩小倍数</param> /// <param name="heightFactor">高度缩小倍数</param> public static void Dilation(Bitmap bmp, float widthFactor, float heightFactor) { BitmapData data = bmp.LockBits(new Rectangle(new Point(), bmp.Size), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb); byte[] buf = new byte[data.Stride * bmp.Height]; Marshal.Copy(data.Scan0, buf, 0, buf.Length); byte[] result = new byte[data.Stride * bmp.Height]; for (int y = 0; y < bmp.Height; y++) { for (int x = 0; x < bmp.Width; x++) { int index = y * data.Stride + x * 3; int rx = (int)(x * widthFactor); int ry = (int)(y * heightFactor); if (rx < 0 || rx >= bmp.Width || ry < 0 || ry >= bmp.Height) { continue; } int rindex = ry * data.Stride + rx * 3; result[index] = buf[rindex]; result[index + 1] = buf[rindex + 1]; result[index + 2] = buf[rindex + 2]; } } Marshal.Copy(result, 0, data.Scan0, result.Length); bmp.UnlockBits(data); } public static void Translate(Bitmap bmp, int xOffset, int yOffset) { BitmapData data = bmp.LockBits(new Rectangle(new Point(), bmp.Size), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb); byte[] buf = new byte[data.Stride * bmp.Height]; Marshal.Copy(data.Scan0, buf, 0, buf.Length); byte[] result = new byte[data.Stride * bmp.Height]; for (int y = 0; y < bmp.Height; y++) { for (int x = 0; x < bmp.Width; x++) { int index = y * data.Stride + x * 3; int rx = x - xOffset; int ry = y - yOffset; if (rx < 0 || rx >= bmp.Width || ry < 0 || ry >= bmp.Height) { continue; } int rindex = ry * data.Stride + rx * 3; result[index] = buf[rindex]; result[index + 1] = buf[rindex + 1]; result[index + 2] = buf[rindex + 2]; } } Marshal.Copy(result, 0, data.Scan0, result.Length); bmp.UnlockBits(data); } public static void Rotate(Bitmap bmp, float angle, Point center) { BitmapData data = bmp.LockBits(new Rectangle(new Point(), bmp.Size), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb); byte[] buf = new byte[data.Stride * bmp.Height]; Marshal.Copy(data.Scan0, buf, 0, buf.Length); byte[] result = new byte[data.Stride * bmp.Height]; for (int y = 0; y < bmp.Height; y++) { for (int x = 0; x < bmp.Width; x++) { int index = y * data.Stride + x * 3; int rx = (int)( (x - center.X) * Math.Cos(angle * Math.PI / 180) - (y - center.Y) * Math.Sin(angle * Math.PI / 180) + center.X ); int ry = (int)( (x - center.X) * Math.Sin(angle * Math.PI / 180) + (y - center.Y) * Math.Cos(angle * Math.PI / 180) + center.Y ); if (rx < 0 || rx >= bmp.Width || ry < 0 || ry >= bmp.Height) { continue; } int rindex = ry * data.Stride + rx * 3; result[index] = buf[rindex]; result[index + 1] = buf[rindex + 1]; result[index + 2] = buf[rindex + 2]; } } Marshal.Copy(result, 0, data.Scan0, result.Length); bmp.UnlockBits(data); } } }
如果要处理图像,Paint.Net 是款不错的软件。