zoukankan      html  css  js  c++  java
  • C#文档图片自动纠偏

    最近找到一个不错的文档图片自动纠偏跟大家分享一下。

    纠偏算法:

      1 public class Deskew
      2     {
      3         // Representation of a line in the image.  
      4         private class HougLine
      5         {
      6             // Count of points in the line.
      7             public int Count;
      8             // Index in Matrix.
      9             public int Index;
     10             // The line is represented as all x,y that solve y*cos(alpha)-x*sin(alpha)=d
     11             public double Alpha;
     12         }
     13 
     14 
     15         // The Bitmap
     16         public Bitmap _internalBmp;
     17 
     18         // The range of angles to search for lines
     19         const double ALPHA_START = -20;
     20         const double ALPHA_STEP = 0.2;
     21         const int STEPS = 40 * 5;
     22         const double STEP = 1;
     23 
     24         // Precalculation of sin and cos.
     25         double[] _sinA;
     26         double[] _cosA;
     27 
     28         // Range of d
     29         double _min;
     30 
     31 
     32         int _count;
     33         // Count of points that fit in a line.
     34         int[] _hMatrix;
     35 
     36         // Calculate the skew angle of the image cBmp.
     37         public double GetSkewAngle()
     38         {
     39             // Hough Transformation
     40             Calc();
     41 
     42             // Top 20 of the detected lines in the image.
     43             HougLine[] hl = GetTop(20);
     44 
     45             // Average angle of the lines
     46             double sum = 0;
     47             int count = 0;
     48             for (int i = 0; i <= 19; i++)
     49             {
     50                 sum += hl[i].Alpha;
     51                 count += 1;
     52             }
     53             return sum / count;
     54         }
     55 
     56         // Calculate the Count lines in the image with most points.
     57         private HougLine[] GetTop(int count)
     58         {
     59             HougLine[] hl = new HougLine[count];
     60 
     61             for (int i = 0; i <= count - 1; i++)
     62             {
     63                 hl[i] = new HougLine();
     64             }
     65             for (int i = 0; i <= _hMatrix.Length - 1; i++)
     66             {
     67                 if (_hMatrix[i] > hl[count - 1].Count)
     68                 {
     69                     hl[count - 1].Count = _hMatrix[i];
     70                     hl[count - 1].Index = i;
     71                     int j = count - 1;
     72                     while (j > 0 && hl[j].Count > hl[j - 1].Count)
     73                     {
     74                         HougLine tmp = hl[j];
     75                         hl[j] = hl[j - 1];
     76                         hl[j - 1] = tmp;
     77                         j -= 1;
     78                     }
     79                 }
     80             }
     81 
     82             for (int i = 0; i <= count - 1; i++)
     83             {
     84                 int dIndex = hl[i].Index / STEPS;
     85                 int alphaIndex = hl[i].Index - dIndex * STEPS;
     86                 hl[i].Alpha = GetAlpha(alphaIndex);
     87                 //hl[i].D = dIndex + _min;
     88             }
     89 
     90             return hl;
     91         }
     92 
     93 
     94         // Hough Transforamtion:
     95         private void Calc()
     96         {
     97             int hMin = _internalBmp.Height / 4;
     98             int hMax = _internalBmp.Height * 3 / 4;
     99 
    100             Init();
    101             for (int y = hMin; y <= hMax; y++)
    102             {
    103                 for (int x = 1; x <= _internalBmp.Width - 2; x++)
    104                 {
    105                     // Only lower edges are considered.
    106                     if (IsBlack(x, y))
    107                     {
    108                         if (!IsBlack(x, y + 1))
    109                         {
    110                             Calc(x, y);
    111                         }
    112                     }
    113                 }
    114             }
    115         }
    116 
    117         // Calculate all lines through the point (x,y).
    118         private void Calc(int x, int y)
    119         {
    120             int alpha;
    121 
    122             for (alpha = 0; alpha <= STEPS - 1; alpha++)
    123             {
    124                 double d = y * _cosA[alpha] - x * _sinA[alpha];
    125                 int calculatedIndex = (int)CalcDIndex(d);
    126                 int index = calculatedIndex * STEPS + alpha;
    127                 try
    128                 {
    129                     _hMatrix[index] += 1;
    130                 }
    131                 catch (Exception ex)
    132                 {
    133                     System.Diagnostics.Debug.WriteLine(ex.ToString());
    134                 }
    135             }
    136         }
    137         private double CalcDIndex(double d)
    138         {
    139             return Convert.ToInt32(d - _min);
    140         }
    141         private bool IsBlack(int x, int y)
    142         {
    143             Color c = _internalBmp.GetPixel(x, y);
    144             double luminance = (c.R * 0.299) + (c.G * 0.587) + (c.B * 0.114);
    145             return luminance < 140;
    146         }
    147 
    148         private void Init()
    149         {
    150             // Precalculation of sin and cos.
    151             _cosA = new double[STEPS];
    152             _sinA = new double[STEPS];
    153 
    154             for (int i = 0; i < STEPS; i++)
    155             {
    156                 double angle = GetAlpha(i) * Math.PI / 180.0;
    157                 _sinA[i] = Math.Sin(angle);
    158                 _cosA[i] = Math.Cos(angle);
    159             }
    160 
    161             // Range of d:            
    162             _min = -_internalBmp.Width;
    163             _count = (int)(2 * (_internalBmp.Width + _internalBmp.Height) / STEP);
    164             _hMatrix = new int[_count * STEPS];
    165 
    166         }
    167 
    168         private static double GetAlpha(int index)
    169         {
    170             return ALPHA_START + index * ALPHA_STEP;
    171         }
    172     }
    View Code

    自己写的调用方法

      1         public static void DeskewImage(string fileName, byte binarizeThreshold)
      2         {
      3             //打开图像
      4             Bitmap bmp = OpenImage(fileName);
      5 
      6             Deskew deskew = new Deskew();
      7             Bitmap tempBmp = CropImage(bmp, bmp.Width / 4, bmp.Height / 4, bmp.Width / 2, bmp.Height / 2);
      8             deskew._internalBmp = BinarizeImage(tempBmp, binarizeThreshold);
      9             double angle = deskew.GetSkewAngle();
     10             bmp = RotateImage(bmp, (float)(-angle));
     11 
     12             //保存图像(需要再还原图片原本的位深度)
     13             SaveImage(bmp, fileName);
     14         }
     15 
     16         /// <summary>
     17         /// 图像剪切
     18         /// </summary>
     19         /// <param name="bmp"></param>
     20         /// <param name="StartX"></param>
     21         /// <param name="StartY"></param>
     22         /// <param name="w"></param>
     23         /// <param name="h"></param>
     24         /// <returns></returns>
     25         private static Bitmap CropImage(Bitmap bmp, int StartX, int StartY, int w, int h)
     26         {
     27             try
     28             {
     29                 Bitmap bmpOut = new Bitmap(w, h, PixelFormat.Format32bppArgb);
     30 
     31                 Graphics g = Graphics.FromImage(bmpOut);
     32                 g.DrawImage(bmp, new Rectangle(0, 0, w, h), new Rectangle(StartX, StartY, w, h), GraphicsUnit.Pixel);
     33                 g.Dispose();
     34 
     35                 return bmpOut;
     36             }
     37             catch
     38             {
     39                 return null;
     40             }
     41         }
     42 
     43 
     44         /// <summary>
     45         /// 图像二值化
     46         /// </summary>
     47         /// <param name="b"></param>
     48         /// <param name="threshold">阈值</param>
     49         private static Bitmap BinarizeImage(Bitmap b, byte threshold)
     50         {
     51             int width = b.Width;
     52             int height = b.Height;
     53             BitmapData data = b.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadWrite, PixelFormat.Format32bppRgb);
     54             unsafe
     55             {
     56                 byte* p = (byte*)data.Scan0;
     57                 int offset = data.Stride - width * 4;
     58                 byte R, G, B, gray;
     59                 for (int y = 0; y < height; y++)
     60                 {
     61                     for (int x = 0; x < width; x++)
     62                     {
     63                         R = p[2];
     64                         G = p[1];
     65                         B = p[0];
     66                         gray = (byte)((R * 19595 + G * 38469 + B * 7472) >> 16);
     67                         if (gray >= threshold)
     68                         {
     69                             p[0] = p[1] = p[2] = 255;
     70                         }
     71                         else
     72                         {
     73                             p[0] = p[1] = p[2] = 0;
     74                         }
     75                         p += 4;
     76                     }
     77                     p += offset;
     78                 }
     79                 b.UnlockBits(data);
     80                 return b;
     81             }
     82         }
     83 
     84         /// <summary>
     85         /// 图像旋转
     86         /// </summary>
     87         /// <param name="bmp"></param>
     88         /// <param name="angle">角度</param>
     89         /// <returns></returns>
     90         private static Bitmap RotateImage(Bitmap bmp, float angle)
     91         {
     92             PixelFormat pixelFormat = bmp.PixelFormat;
     93             PixelFormat pixelFormatOld = pixelFormat;
     94             if (bmp.Palette.Entries.Count() > 0)
     95             {
     96                 pixelFormat = PixelFormat.Format24bppRgb;
     97             }
     98 
     99             Bitmap tmpBitmap = new Bitmap(bmp.Width, bmp.Height, pixelFormat);
    100             tmpBitmap.SetResolution(bmp.HorizontalResolution, bmp.VerticalResolution);
    101             Graphics g = Graphics.FromImage(tmpBitmap);
    102             try
    103             {
    104                 g.FillRectangle(Brushes.White, 0, 0, bmp.Width, bmp.Height);
    105                 g.RotateTransform(angle);
    106                 g.DrawImage(bmp, 0, 0);
    107             }
    108             catch
    109             {
    110             }
    111             finally
    112             {
    113                 g.Dispose();
    114             }
    115 
    116             if (pixelFormatOld == PixelFormat.Format8bppIndexed) tmpBitmap = CopyTo8bpp(tmpBitmap);
    117             else if (pixelFormatOld == PixelFormat.Format1bppIndexed) tmpBitmap = CopyTo1bpp(tmpBitmap);
    118 
    119             return tmpBitmap;
    120         }
    View Code

    在最后进行图片选择时,位深度为1、4、8的索引图片是没办法直接用Graphics进行旋转操作的,需要图像的PixelFormat再做旋转。

    现在只实现位深度为1和8的索引图片还原。

      1 private static Bitmap CopyTo1bpp(Bitmap b)
      2         {
      3             int w = b.Width, h = b.Height; Rectangle r = new Rectangle(0, 0, w, h);
      4             if (b.PixelFormat != PixelFormat.Format32bppPArgb)
      5             {
      6                 Bitmap temp = new Bitmap(w, h, PixelFormat.Format32bppPArgb);
      7                 temp.SetResolution(b.HorizontalResolution, b.VerticalResolution);
      8                 Graphics g = Graphics.FromImage(temp);
      9                 g.DrawImage(b, r, 0, 0, w, h, GraphicsUnit.Pixel);
     10                 g.Dispose();
     11                 b = temp;
     12             }
     13             BitmapData bdat = b.LockBits(r, ImageLockMode.ReadOnly, b.PixelFormat);
     14             Bitmap b0 = new Bitmap(w, h, PixelFormat.Format1bppIndexed);
     15             b0.SetResolution(b.HorizontalResolution, b.VerticalResolution);
     16             BitmapData b0dat = b0.LockBits(r, ImageLockMode.ReadWrite, PixelFormat.Format1bppIndexed);
     17             for (int y = 0; y < h; y++)
     18             {
     19                 for (int x = 0; x < w; x++)
     20                 {
     21                     int index = y * bdat.Stride + (x * 4);
     22                     if (Color.FromArgb(Marshal.ReadByte(bdat.Scan0, index + 2), Marshal.ReadByte(bdat.Scan0, index + 1), Marshal.ReadByte(bdat.Scan0, index)).GetBrightness() > 0.5f)
     23                     {
     24                         int index0 = y * b0dat.Stride + (x >> 3);
     25                         byte p = Marshal.ReadByte(b0dat.Scan0, index0);
     26                         byte mask = (byte)(0x80 >> (x & 0x7));
     27                         Marshal.WriteByte(b0dat.Scan0, index0, (byte)(p | mask));
     28                     }
     29                 }
     30             }
     31             b0.UnlockBits(b0dat);
     32             b.UnlockBits(bdat);
     33             return b0;
     34         }
     35 
     36         private static Bitmap CopyTo8bpp(Bitmap bmp)
     37         {
     38             if (bmp == null) return null;
     39 
     40             Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
     41             BitmapData bmpData = bmp.LockBits(rect, ImageLockMode.ReadOnly, bmp.PixelFormat);
     42 
     43             int width = bmpData.Width;
     44             int height = bmpData.Height;
     45             int stride = bmpData.Stride;
     46             int offset = stride - width * 3;
     47             IntPtr ptr = bmpData.Scan0;
     48             int scanBytes = stride * height;
     49 
     50             int posScan = 0, posDst = 0;
     51             byte[] rgbValues = new byte[scanBytes];
     52             Marshal.Copy(ptr, rgbValues, 0, scanBytes);
     53             byte[] grayValues = new byte[width * height];
     54 
     55             for (int i = 0; i < height; i++)
     56             {
     57                 for (int j = 0; j < width; j++)
     58                 {
     59                     double temp = rgbValues[posScan++] * 0.11 +
     60                         rgbValues[posScan++] * 0.59 +
     61                         rgbValues[posScan++] * 0.3;
     62                     grayValues[posDst++] = (byte)temp;
     63                 }
     64                 posScan += offset;
     65             }
     66 
     67             Marshal.Copy(rgbValues, 0, ptr, scanBytes);
     68             bmp.UnlockBits(bmpData);
     69 
     70             Bitmap bitmap = new Bitmap(width, height, PixelFormat.Format8bppIndexed);
     71             bitmap.SetResolution(bmp.HorizontalResolution, bmp.VerticalResolution);
     72             BitmapData bitmapData = bitmap.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, PixelFormat.Format8bppIndexed);
     73 
     74             int offset0 = bitmapData.Stride - bitmapData.Width;
     75             int scanBytes0 = bitmapData.Stride * bitmapData.Height;
     76             byte[] rawValues = new byte[scanBytes0];
     77 
     78             int posSrc = 0;
     79             posScan = 0;
     80             for (int i = 0; i < height; i++)
     81             {
     82                 for (int j = 0; j < width; j++)
     83                 {
     84                     rawValues[posScan++] = grayValues[posSrc++];
     85                 }
     86                 posScan += offset0;
     87             }
     88 
     89             Marshal.Copy(rawValues, 0, bitmapData.Scan0, scanBytes0);
     90             bitmap.UnlockBits(bitmapData);
     91 
     92             ColorPalette palette;
     93             using (Bitmap bmp0 = new Bitmap(1, 1, PixelFormat.Format8bppIndexed))
     94             {
     95                 palette = bmp0.Palette;
     96             }
     97             for (int i = 0; i < 256; i++)
     98             {
     99                 palette.Entries[i] = Color.FromArgb(i, i, i);
    100             }
    101             bitmap.Palette = palette;
    102 
    103             return bitmap;
    104         }
    View Code

    第一次发博,如有误的地方请大湿们多多指点。

  • 相关阅读:
    jvm 更多链接
    JVM 内存初学 (堆(heap)、栈(stack)和方法区(method) )
    python 排序
    python 第K个语法符号
    python conf.ini 文件的使用
    python 表示数字
    window 下安装redis
    python redis相关操作
    python 矩阵乘法
    python 查找两个字符串a,b中的最长公共子串
  • 原文地址:https://www.cnblogs.com/fireshadow23/p/3600466.html
Copyright © 2011-2022 走看看