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;
}