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

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

你可能感兴趣的:(C#)