c#文档图片自动纠偏
public class Deskew
{
// Representation of a line in the image.
private class HougLine
{
// Count of points in the line.
public int Count;
// Index in Matrix.
public int Index;
// The line is represented as all x,y that solve y*cos(alpha)-x*sin(alpha)=d
public double Alpha;
}
// The Bitmap
public Bitmap _internalBmp;
// The range of angles to search for lines
const double ALPHA_START = -20;
const double ALPHA_STEP = 0.2;
const int STEPS = 40 * 5;
const double STEP = 1;
// Precalculation of sin and cos.
double[] _sinA;
double[] _cosA;
// Range of d
double _min;
int _count;
// Count of points that fit in a line.
int[] _hMatrix;
// Calculate the skew angle of the image cBmp.
public double GetSkewAngle()
{
// Hough Transformation
Calc();
// Top 20 of the detected lines in the image.
HougLine[] hl = GetTop(20);
// Average angle of the lines
double sum = 0;
int count = 0;
for (int i = 0; i <= 19; i++)
{
sum += hl[i].Alpha;
count += 1;
}
return sum / count;
}
// Calculate the Count lines in the image with most points.
private HougLine[] GetTop(int count)
{
HougLine[] hl = new HougLine[count];
for (int i = 0; i <= count - 1; i++)
{
hl[i] = new HougLine();
}
for (int i = 0; i <= _hMatrix.Length - 1; i++)
{
if (_hMatrix[i] > hl[count - 1].Count)
{
hl[count - 1].Count = _hMatrix[i];
hl[count - 1].Index = i;
int j = count - 1;
while (j > 0 && hl[j].Count > hl[j - 1].Count)
{
HougLine tmp = hl[j];
hl[j] = hl[j - 1];
hl[j - 1] = tmp;
j -= 1;
}
}
}
for (int i = 0; i <= count - 1; i++)
{
int dIndex = hl[i].Index / STEPS;
int alphaIndex = hl[i].Index - dIndex * STEPS;
hl[i].Alpha = GetAlpha(alphaIndex);
//hl[i].D = dIndex + _min;
}
return hl;
}
// Hough Transforamtion:
private void Calc()
{
int hMin = _internalBmp.Height / 4;
int hMax = _internalBmp.Height * 3 / 4;
Init();
for (int y = hMin; y <= hMax; y++)
{
for (int x = 1; x <= _internalBmp.Width - 2; x++)
{
// Only lower edges are considered.
if (IsBlack(x, y))
{
if (!IsBlack(x, y + 1))
{
Calc(x, y);
}
}
}
}
}
// Calculate all lines through the point (x,y).
private void Calc(int x, int y)
{
int alpha;
for (alpha = 0; alpha <= STEPS - 1; alpha++)
{
double d = y * _cosA[alpha] - x * _sinA[alpha];
int calculatedIndex = (int)CalcDIndex(d);
int index = calculatedIndex * STEPS + alpha;
try
{
_hMatrix[index] += 1;
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex.ToString());
}
}
}
private double CalcDIndex(double d)
{
return Convert.ToInt32(d - _min);
}
private bool IsBlack(int x, int y)
{
Color c = _internalBmp.GetPixel(x, y);
double luminance = (c.R * 0.299) + (c.G * 0.587) + (c.B * 0.114);
return luminance < 140;
}
private void Init()
{
// Precalculation of sin and cos.
_cosA = new double[STEPS];
_sinA = new double[STEPS];
for (int i = 0; i < STEPS; i++)
{
double angle = GetAlpha(i) * Math.PI / 180.0;
_sinA[i] = Math.Sin(angle);
_cosA[i] = Math.Cos(angle);
}
// Range of d:
_min = -_internalBmp.Width;
_count = (int)(2 * (_internalBmp.Width + _internalBmp.Height) / STEP);
_hMatrix = new int[_count * STEPS];
}
private static double GetAlpha(int index)
{
return ALPHA_START + index * ALPHA_STEP;
}
}
自己写的调用方法
public static void DeskewImage(string fileName, byte binarizeThreshold)
{
//打开图像
Bitmap bmp = OpenImage(fileName);
Deskew deskew = new Deskew();
Bitmap tempBmp = CropImage(bmp, bmp.Width / 4, bmp.Height / 4, bmp.Width / 2, bmp.Height / 2);
deskew._internalBmp = BinarizeImage(tempBmp, binarizeThreshold);
double angle = deskew.GetSkewAngle();
bmp = RotateImage(bmp, (float)(-angle));
//保存图像(需要再还原图片原本的位深度)
SaveImage(bmp, fileName);
}
/// <summary>
/// 图像剪切
/// </summary>
/// <param name="bmp"></param>
/// <param name="StartX"></param>
/// <param name="StartY"></param>
/// <param name="w"></param>
/// <param name="h"></param>
/// <returns></returns>
private static Bitmap CropImage(Bitmap bmp, int StartX, int StartY, int w, int h)
{
try
{
Bitmap bmpOut = new Bitmap(w, h, PixelFormat.Format32bppArgb);
Graphics g = Graphics.FromImage(bmpOut);
g.DrawImage(bmp, new Rectangle(0, 0, w, h), new Rectangle(StartX, StartY, w, h), GraphicsUnit.Pixel);
g.Dispose();
return bmpOut;
}
catch
{
return null;
}
}
/// <summary>
/// 图像二值化
/// </summary>
/// <param name="b"></param>
/// <param name="threshold">阈值</param>
private static Bitmap BinarizeImage(Bitmap b, byte threshold)
{
int width = b.Width;
int height = b.Height;
BitmapData data = b.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadWrite, PixelFormat.Format32bppRgb);
unsafe
{
byte* p = (byte*)data.Scan0;
int offset = data.Stride - width * 4;
byte R, G, B, gray;
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
R = p[2];
G = p[1];
B = p[0];
gray = (byte)((R * 19595 + G * 38469 + B * 7472) >> 16);
if (gray >= threshold)
{
p[0] = p[1] = p[2] = 255;
}
else
{
p[0] = p[1] = p[2] = 0;
}
p += 4;
}
p += offset;
}
b.UnlockBits(data);
return b;
}
}
/// <summary>
/// 图像旋转
/// </summary>
/// <param name="bmp"></param>
/// <param name="angle">角度</param>
/// <returns></returns>
private static Bitmap RotateImage(Bitmap bmp, float angle)
{
PixelFormat pixelFormat = bmp.PixelFormat;
PixelFormat pixelFormatOld = pixelFormat;
if (bmp.Palette.Entries.Count() > 0)
{
pixelFormat = PixelFormat.Format24bppRgb;
}
Bitmap tmpBitmap = new Bitmap(bmp.Width, bmp.Height, pixelFormat);
tmpBitmap.SetResolution(bmp.HorizontalResolution, bmp.VerticalResolution);
Graphics g = Graphics.FromImage(tmpBitmap);
try
{
g.FillRectangle(Brushes.White, 0, 0, bmp.Width, bmp.Height);
g.RotateTransform(angle);
g.DrawImage(bmp, 0, 0);
}
catch
{
}
finally
{
g.Dispose();
}
if (pixelFormatOld == PixelFormat.Format8bppIndexed) tmpBitmap = CopyTo8bpp(tmpBitmap);
else if (pixelFormatOld == PixelFormat.Format1bppIndexed) tmpBitmap = CopyTo1bpp(tmpBitmap);
return tmpBitmap;
}
在最后进行图片选择时,位深度为1、4、8的索引图片是没办法直接用Graphics进行旋转操作的,需要图像的PixelFormat再做旋转。
现在只实现位深度为1和8的索引图片还原。
private static Bitmap CopyTo1bpp(Bitmap b)
{
int w = b.Width, h = b.Height; Rectangle r = new Rectangle(0, 0, w, h);
if (b.PixelFormat != PixelFormat.Format32bppPArgb)
{
Bitmap temp = new Bitmap(w, h, PixelFormat.Format32bppPArgb);
temp.SetResolution(b.HorizontalResolution, b.VerticalResolution);
Graphics g = Graphics.FromImage(temp);
g.DrawImage(b, r, 0, 0, w, h, GraphicsUnit.Pixel);
g.Dispose();
b = temp;
}
BitmapData bdat = b.LockBits(r, ImageLockMode.ReadOnly, b.PixelFormat);
Bitmap b0 = new Bitmap(w, h, PixelFormat.Format1bppIndexed);
b0.SetResolution(b.HorizontalResolution, b.VerticalResolution);
BitmapData b0dat = b0.LockBits(r, ImageLockMode.ReadWrite, PixelFormat.Format1bppIndexed);
for (int y = 0; y < h; y++)
{
for (int x = 0; x < w; x++)
{
int index = y * bdat.Stride + (x * 4);
if (Color.FromArgb(Marshal.ReadByte(bdat.Scan0, index + 2), Marshal.ReadByte(bdat.Scan0, index + 1), Marshal.ReadByte(bdat.Scan0, index)).GetBrightness() > 0.5f)
{
int index0 = y * b0dat.Stride + (x >> 3);
byte p = Marshal.ReadByte(b0dat.Scan0, index0);
byte mask = (byte)(0x80 >> (x & 0x7));
Marshal.WriteByte(b0dat.Scan0, index0, (byte)(p | mask));
}
}
}
b0.UnlockBits(b0dat);
b.UnlockBits(bdat);
return b0;
}
private static Bitmap CopyTo8bpp(Bitmap bmp)
{
if (bmp == null) return null;
Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
BitmapData bmpData = bmp.LockBits(rect, ImageLockMode.ReadOnly, bmp.PixelFormat);
int width = bmpData.Width;
int height = bmpData.Height;
int stride = bmpData.Stride;
int offset = stride - width * 3;
IntPtr ptr = bmpData.Scan0;
int scanBytes = stride * height;
int posScan = 0, posDst = 0;
byte[] rgbValues = new byte[scanBytes];
Marshal.Copy(ptr, rgbValues, 0, scanBytes);
byte[] grayValues = new byte[width * height];
for (int i = 0; i < height; i++)
{
for (int j = 0; j < width; j++)
{
double temp = rgbValues[posScan++] * 0.11 +
rgbValues[posScan++] * 0.59 +
rgbValues[posScan++] * 0.3;
grayValues[posDst++] = (byte)temp;
}
posScan += offset;
}
Marshal.Copy(rgbValues, 0, ptr, scanBytes);
bmp.UnlockBits(bmpData);
Bitmap bitmap = new Bitmap(width, height, PixelFormat.Format8bppIndexed);
bitmap.SetResolution(bmp.HorizontalResolution, bmp.VerticalResolution);
BitmapData bitmapData = bitmap.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, PixelFormat.Format8bppIndexed);
int offset0 = bitmapData.Stride - bitmapData.Width;
int scanBytes0 = bitmapData.Stride * bitmapData.Height;
byte[] rawValues = new byte[scanBytes0];
int posSrc = 0;
posScan = 0;
for (int i = 0; i < height; i++)
{
for (int j = 0; j < width; j++)
{
rawValues[posScan++] = grayValues[posSrc++];
}
posScan += offset0;
}
Marshal.Copy(rawValues, 0, bitmapData.Scan0, scanBytes0);
bitmap.UnlockBits(bitmapData);
ColorPalette palette;
using (Bitmap bmp0 = new Bitmap(1, 1, PixelFormat.Format8bppIndexed))
{
palette = bmp0.Palette;
}
for (int i = 0; i < 256; i++)
{
palette.Entries[i] = Color.FromArgb(i, i, i);
}
bitmap.Palette = palette;
return bitmap;
}
相关文章
- 我们在使用C#做项目的时候,基本上都需要制作登录界面,那么今天我们就来一步步看看,如果简单的实现登录界面呢,本文给出2个例子,由简入难,希望大家能够喜欢。...2020-06-25
- 这篇文章主要介绍了C# 字段和属性的的相关资料,文中示例代码非常详细,供大家参考和学习,感兴趣的朋友可以了解下...2020-11-03
使用PHP+JavaScript将HTML页面转换为图片的实例分享
这篇文章主要介绍了使用PHP+JavaScript将HTML元素转换为图片的实例分享,文后结果的截图只能体现出替换的字体,也不能说将静态页面转为图片可以加快加载,只是这种做法比较interesting XD需要的朋友可以参考下...2016-04-19- 这篇文章主要介绍了C#中截取字符串的的基本方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-11-03
- 本文给大家分享C#连接SQL数据库和查询数据功能的操作技巧,本文通过图文并茂的形式给大家介绍的非常详细,需要的朋友参考下吧...2021-05-17
- 这篇文章主要介绍了C#实现简单的Http请求的方法,以实例形式较为详细的分析了C#实现Http请求的具体方法,需要的朋友可以参考下...2020-06-25
- 本文主要介绍了C#中new的几种用法,具有很好的参考价值,下面跟着小编一起来看下吧...2020-06-25
使用Visual Studio2019创建C#项目(窗体应用程序、控制台应用程序、Web应用程序)
这篇文章主要介绍了使用Visual Studio2019创建C#项目(窗体应用程序、控制台应用程序、Web应用程序),小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧...2020-06-25- 这篇文章主要介绍了C#开发Windows窗体应用程序的简单操作步骤,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2021-04-12
- php如何实现抓取网页图片,相较于手动的粘贴复制,使用小程序要方便快捷多了,喜欢编程的人总会喜欢制作一些简单有用的小软件,最近就参考了网上一个php抓取图片代码,封装了一个php远程抓取图片的类,测试了一下,效果还不错分享...2015-10-30
- 这篇文章主要介绍了C#从数据库读取图片并保存的方法,帮助大家更好的理解和使用c#,感兴趣的朋友可以了解下...2021-01-16
- 今天小编在这里就来给各位Photoshop的这一款软件的使用者们来说说把古装美女图片转为细腻的工笔画效果的制作教程,各位想知道方法的使用者们,那么下面就快来跟着小编一...2016-09-14
- 这篇文章主要介绍了Python 图片转数组,二进制互转操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2021-03-09
- 最近做一个小项目不可避免的需要前端脚本与后台进行交互。由于是在asp.net中实现,故问题演化成asp.net中jiavascript与后台c#如何进行交互。...2020-06-25
- 这篇文章主要用实例讲解C#递归算法的概念以及用法,文中代码非常详细,帮助大家更好的参考和学习,感兴趣的朋友可以了解下...2020-06-25
- 本文通过例子,讲述了C++调用C#的DLL程序的方法,作出了以下总结,下面就让我们一起来学习吧。...2020-06-25
- 轻松学习C#的基础入门,了解C#最基本的知识点,C#是一种简洁的,类型安全的一种完全面向对象的开发语言,是Microsoft专门基于.NET Framework平台开发的而量身定做的高级程序设计语言,需要的朋友可以参考下...2020-06-25
- 本文主要介绍了C#变量命名规则小结,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2021-09-09
- 这篇文章主要介绍了c#中(&&,||)与(&,|)的区别详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-06-25
- 本文主要介绍了C# 中取绝对值的函数。具有很好的参考价值。下面跟着小编一起来看下吧...2020-06-25