zoukankan      html  css  js  c++  java
  • 记录一个优秀的图像二值化代码

    #region 二值化02
    
            public Bitmap binaryzation(Bitmap srcBitmap, Bitmap dstBitmap)
            {
                int threshold = 0;
                Byte[,] BinaryArray = ToBinaryArray(srcBitmap, out threshold);
                dstBitmap = BinaryArrayToBinaryBitmap(BinaryArray);
                return dstBitmap;
            }
    
            /// <summary>
            /// 全局阈值图像二值化
            /// </summary>
            /// <param name="bmp">原始图像</param>
            /// <param name="method">二值化方法</param>
            /// <param name="threshold">输出:全局阈值</param>
            /// <returns>二值化后的图像数组</returns>       
            public static Byte[,] ToBinaryArray(Bitmap bmp, out int threshold)
            {   // 位图转换为灰度数组
                Byte[,] GrayArray = ToGrayArray(bmp);
    
                // 计算全局阈值
                threshold = OtsuThreshold(GrayArray);
    
                // 根据阈值进行二值化
                int PixelHeight = bmp.Height;
                int PixelWidth = bmp.Width;
                Byte[,] BinaryArray = new Byte[PixelHeight, PixelWidth];
                for (int i = 0; i < PixelHeight; i++)
                {
                    for (int j = 0; j < PixelWidth; j++)
                    {
                        BinaryArray[i, j] = Convert.ToByte((GrayArray[i, j] > threshold) ? 255 : 0);
                    }
                }
    
                return BinaryArray;
            }
    
            /// <summary>
            /// 大津法计算阈值
            /// </summary>
            /// <param name="grayArray">灰度数组</param>
            /// <returns>二值化阈值</returns>
            public static int OtsuThreshold(Byte[,] grayArray)
            {   // 建立统计直方图
                int[] Histogram = new int[256];
                Array.Clear(Histogram, 0, 256);     // 初始化
                foreach (Byte b in grayArray)
                {
                    Histogram[b]++;                 // 统计直方图
                }
    
                // 总的质量矩和图像点数
                int SumC = grayArray.Length;    // 总的图像点数
                Double SumU = 0;                  // 双精度避免方差运算中数据溢出
                for (int i = 1; i < 256; i++)
                {
                    SumU += i * Histogram[i];     // 总的质量矩               
                }
    
                // 灰度区间
                int MinGrayLevel = Array.FindIndex(Histogram, NonZero);       // 最小灰度值
                int MaxGrayLevel = Array.FindLastIndex(Histogram, NonZero);   // 最大灰度值
    
                // 计算最大类间方差
                int Threshold = MinGrayLevel;
                Double MaxVariance = 0.0;       // 初始最大方差
                Double U0 = 0;                  // 初始目标质量矩
                int C0 = 0;                   // 初始目标点数
                for (int i = MinGrayLevel; i < MaxGrayLevel; i++)
                {
                    if (Histogram[i] == 0) continue;
    
                    // 目标的质量矩和点数               
                    U0 += i * Histogram[i];
                    C0 += Histogram[i];
    
                    // 计算目标和背景的类间方差
                    Double Diference = U0 * SumC - SumU * C0;
                    Double Variance = Diference * Diference / C0 / (SumC - C0); // 方差
                    if (Variance > MaxVariance)
                    {
                        MaxVariance = Variance;
                        Threshold = i;
                    }
                }
    
                // 返回类间方差最大阈值
                return Threshold;
            }
    
            /// <summary>
            /// 检测非零值
            /// </summary>
            /// <param name="value">要检测的数值</param>
            /// <returns>
            ///     true:非零
            ///     false:零
            /// </returns>
            private static Boolean NonZero(int value)
            {
                return (value != 0) ? true : false;
            }
    
            /// <summary>
            /// 将位图转换为灰度数组(256级灰度)
            /// </summary>
            /// <param name="bmp">原始位图</param>
            /// <returns>灰度数组</returns>
            public static Byte[,] ToGrayArray(Bitmap bmp)
            {
                int PixelHeight = bmp.Height; // 图像高度
                int PixelWidth = bmp.Width;   // 图像宽度
                int Stride = ((PixelWidth * 3 + 3) >> 2) << 2;    // 跨距宽度
                Byte[] Pixels = new Byte[PixelHeight * Stride];
    
                // 锁定位图到系统内存
                BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0, PixelWidth, PixelHeight), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
                Marshal.Copy(bmpData.Scan0, Pixels, 0, Pixels.Length);  // 从非托管内存拷贝数据到托管内存
                bmp.UnlockBits(bmpData);    // 从系统内存解锁位图
    
                // 将像素数据转换为灰度数组
                Byte[,] GrayArray = new Byte[PixelHeight, PixelWidth];
                for (int i = 0; i < PixelHeight; i++)
                {
                    int Index = i * Stride;
                    for (int j = 0; j < PixelWidth; j++)
                    {
                        GrayArray[i, j] = Convert.ToByte((Pixels[Index + 2] * 19595 + Pixels[Index + 1] * 38469 + Pixels[Index] * 7471 + 32768) >> 16);
                        Index += 3;
                    }
                }
    
                return GrayArray;
            }
    
            /// <summary>
            /// 将二值化数组转换为二值化图像
            /// </summary>
            /// <param name="binaryArray">二值化数组</param>
            /// <returns>二值化图像</returns>
            public static Bitmap BinaryArrayToBinaryBitmap(Byte[,] binaryArray)
            {   // 将二值化数组转换为二值化数据
                int PixelHeight = binaryArray.GetLength(0);
                int PixelWidth = binaryArray.GetLength(1);
                int Stride = ((PixelWidth + 31) >> 5) << 2;
                //int Stride = PixelWidth/8+(4- (PixelWidth / 8)%4);
                Byte[] Pixels = new Byte[PixelHeight * Stride];
                for (int i = 0; i < PixelHeight; i++)
                {
                    int Base = i * Stride;
                    for (int j = 0; j < PixelWidth; j++)
                    {
                        if (binaryArray[i, j] != 0)
                        {
                            Pixels[Base + (j >> 3)] |= Convert.ToByte(0x80 >> (j & 0x7));
                        }
                    }
                }
    
                // 创建黑白图像
                Bitmap BinaryBmp = new Bitmap(PixelWidth, PixelHeight, PixelFormat.Format1bppIndexed);
    
                // 设置调色表
                ColorPalette cp = BinaryBmp.Palette;
                cp.Entries[0] = Color.Black;    // 黑色
                cp.Entries[1] = Color.White;    // 白色
                BinaryBmp.Palette = cp;
    
                // 设置位图图像特性
                BitmapData BinaryBmpData = BinaryBmp.LockBits(new Rectangle(0, 0, PixelWidth, PixelHeight), ImageLockMode.WriteOnly, PixelFormat.Format1bppIndexed);
                Marshal.Copy(Pixels, 0, BinaryBmpData.Scan0, Pixels.Length);
                BinaryBmp.UnlockBits(BinaryBmpData);
    
                return BinaryBmp;
            }
    
            #endregion
         其中将二值化数组转换为二值化图像的算法很有意思
    
          求跨距宽度这一步怎么也想不通为什么 int Stride = ((PixelWidth + 31) >> 5) << 2;
    下面这个是一般的写法,很容易懂 参考https://www.cnblogs.com/dearzhoubi/p/8655326.html //int Stride = PixelWidth/8+(4- (PixelWidth / 8)%4);
    Pixels[Base + (j >> 3)] |= Convert.ToByte(0x80 >> (j & 0x7));
    ****j&0x7实际上就是对j进行对8取余
    ****0x7二进制就是0000 0111
    ****比如j=18,二进制j=0001 0010,j&0x7=0000 0010,j=18对8取余就是2,也就是说二值数组中j=18这个位置的数据在二值图像中的存储位置是2个字节后第三个字节的第2个位置
    ****也就是要把0x80向右移动2位,0x80二进制是1000 0000,右移2位是0010 0000,
    ****然后和当前正在填充的字节进行按位或|,就可以把这一位数据填充进去
    ****实在是不熟悉这种二进制的这种移位操作,很神奇。




  • 相关阅读:
    expect 批量自动部署ssh 免密登陆 之 二
    expect 批量自动部署ssh 免密登陆
    Haproxy_haproxy.cfg
    gitlab 权限说明
    Docker限制容器日志大小
    Docker 常用指令
    CentOS下安装Docker
    Vue项目创建build后可修改的配置文件(接口地址配置)
    SpringBoot+Mybatis-plus多数据源配置(MySQL、Sqlserver)
    JQuery序列化表单排除空值的问题
  • 原文地址:https://www.cnblogs.com/dearzhoubi/p/9854653.html
Copyright © 2011-2022 走看看