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);
        }
    }
}
BitmapHelper

如果要处理图像,Paint.Net 是款不错的软件。

posted on 2013-10-18 19:28  x01  阅读(989)  评论(0编辑  收藏  举报

导航