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 是款不错的软件。

你可能感兴趣的:(bitmap)