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

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

原文地址:https://www.cnblogs.com/fireshadow23/p/3600466.html