add auto straighten option for scanned pages

Conflicts:

	koptconfig.lua
	koptreader.lua
This commit is contained in:
chrox
2012-10-27 16:03:49 +08:00
committed by Qingping Hou
parent 148dc48556
commit 2c3b4fa968
4 changed files with 391 additions and 10 deletions

9
djvu.c
View File

@@ -482,12 +482,13 @@ static int reflowPage(lua_State *L) {
double line_spacing = luaL_checknumber(L, 8);
double word_spacing = luaL_checknumber(L, 9);
int text_wrap = luaL_checkint(L, 10);
int justification = luaL_checkint(L, 11);
int columns = luaL_checkint(L, 12);
double contrast = luaL_checknumber(L, 13);
int straighten = luaL_checkint(L, 11);
int justification = luaL_checkint(L, 12);
int columns = luaL_checkint(L, 13);
double contrast = luaL_checknumber(L, 14);
k2pdfopt_set_params(width, height, font_size, page_margin, line_spacing, word_spacing, \
text_wrap, justification, columns, contrast);
text_wrap, straighten, justification, columns, contrast);
k2pdfopt_djvu_reflow(page->page_ref, page->doc->context, mode, page->doc->pixelformat);
k2pdfopt_rfbmp_size(&width, &height);
k2pdfopt_rfbmp_zoom(&dc->zoom);

View File

@@ -82,6 +82,7 @@
/* bmp.c */
#define WILLUSBITMAP_TYPE_NATIVE 0
#define WILLUSBITMAP_TYPE_WIN32 1
#define BOUND(x,xmin,xmax) if ((x)<(xmin)) (x)=(xmin); else { if ((x)>(xmax)) (x)=(xmax); }
#ifdef PI
#undef PI
@@ -218,6 +219,7 @@ static double dst_min_figure_height_in = 0.75;
static int dst_fulljustify = -1; // 0 = no, 1 = yes
static int dst_color = 0;
static int dst_landscape = 0;
static int src_autostraighten = 0;
static double dst_mar = 0.06;
static double dst_martop = -1.0;
static double dst_marbot = -1.0;
@@ -398,6 +400,8 @@ static int bmp_resample(WILLUSBITMAP *dest, WILLUSBITMAP *src, double x1,
double y1, double x2, double y2, int newwidth, int newheight);
static void bmp_contrast_adjust(WILLUSBITMAP *dest,WILLUSBITMAP *src,double contrast);
static void bmp_convert_to_greyscale_ex(WILLUSBITMAP *dst, WILLUSBITMAP *src);
static double bmp_autostraighten(WILLUSBITMAP *src, WILLUSBITMAP *srcgrey, int white,
double maxdegrees, double mindegrees, int debug);
static int bmpmupdf_pixmap_to_bmp(WILLUSBITMAP *bmp, fz_context *ctx,
fz_pixmap *pixmap);
static void handle(int wait, ddjvu_context_t *ctx);
@@ -453,6 +457,15 @@ static void k2pdfopt_reflow_bmp(MASTERINFO *masterinfo, WILLUSBITMAP *src) {
adjust_contrast(src, srcgrey, &white);
white_margins(src, srcgrey);
if (erase_vertical_lines > 0)
bmp_detect_vertical_lines(srcgrey, src, (double) src_dpi, 0.005, 0.25,
min_column_height_inches, src_autostraighten, white);
if (src_autostraighten > 0.) {
double rot;
rot = bmp_autostraighten(src, srcgrey, white, src_autostraighten, 0.1, debug);
pageinfo->page_rot_deg += rot;
}
region.r1 = 0;
region.r2 = srcgrey->height - 1;
region.c1 = 0;
@@ -475,7 +488,7 @@ static void k2pdfopt_reflow_bmp(MASTERINFO *masterinfo, WILLUSBITMAP *src) {
void k2pdfopt_set_params(int bb_width, int bb_height, \
double font_size, double page_margin, \
double line_space, double word_space, \
int wrapping, int justification, \
int wrapping, int straighten, int justification, \
int columns, double contrast) {
dst_userwidth = bb_width; // dst_width is adjusted in adjust_params_init
dst_userheight = bb_height;
@@ -483,6 +496,7 @@ void k2pdfopt_set_params(int bb_width, int bb_height, \
vertical_line_spacing = line_space;
word_spacing = word_space;
text_wrap = wrapping;
src_autostraighten = straighten;
max_columns = columns;
gamma_correction = contrast; // contrast is only used by k2pdfopt_mupdf_reflow
@@ -6426,6 +6440,371 @@ static void bmp_convert_to_greyscale_ex(WILLUSBITMAP *dst, WILLUSBITMAP *src)
dst->bpp = 8; /* Possibly restore dst->bpp to 8 since src & dst may be the same. */
}
/*
** Bitmap is assumed to be grayscale
*/
static double bmp_row_by_row_stdev(WILLUSBITMAP *bmp, int ccount,
int whitethresh, double theta_radians)
{
int dc1, dc2, c1, c2;
int r, n, nn, dw;
double tanth, csum, csumsq, stdev;
c1 = bmp->width / 15.;
c2 = bmp->width - c1;
dw = (int) ((c2 - c1) / ccount + .5);
if (dw < 1)
dw = 1;
tanth = -tan(theta_radians);
dc1 = (int) (tanth * bmp->width);
if (dc1 < 0) {
dc1 = 1 - dc1;
dc2 = 0;
} else {
dc2 = -dc1 - 1;
dc1 = 0;
}
dc1 += bmp->height / 15.;
dc2 -= bmp->height / 15.;
csum = csumsq = 0.;
n = 0;
for (r = dc1 + 1; r < bmp->height + dc2 - 1; r++) {
int c, count, r0last;
double dcount;
unsigned char *p;
r0last = 0;
p = bmp_rowptr_from_top(bmp, r0last);
for (nn = count = 0, c = c1; c < c2; c += dw) {
int r0;
r0 = r + tanth * c;
if (r0 < 0 || r0 >= bmp->height)
continue;
if (r0 != r0last) {
r0last = r0;
p = bmp_rowptr_from_top(bmp, r0last);
}
nn++;
if (p[c] < whitethresh)
count++;
}
dcount = 100. * count / nn;
csum += dcount;
csumsq += dcount * dcount;
n++;
}
stdev = sqrt(fabs((csum / n) * (csum / n) - csumsq / n));
return (stdev);
}
/*
** y0 = 0 ==> bottom row!
*/
static void bmp_pix_vali(WILLUSBITMAP *bmp, int x0, int y0, int *r, int *g, int *b)
{
unsigned char *p;
int rr, gg, bb;
p = bmp_rowptr_from_top(bmp, bmp->height - 1 - y0);
p = &p[x0 * (bmp->bpp >> 3)];
RGBGET(bmp, p, rr, gg, bb);
(*r) = rr;
(*g) = gg;
(*b) = bb;
}
/*
** y0 = 0 ==> bottom row!
*/
static int bmp_grey_pix_vali(WILLUSBITMAP *bmp, int x0, int y0)
{
unsigned char *p;
int r, g, b;
p = bmp_rowptr_from_top(bmp, bmp->height - 1 - y0);
p = &p[x0 * (bmp->bpp >> 3)];
RGBGET(bmp, p, r, g, b);
return (bmp8_greylevel_convert(r, g, b));
}
/*
** Return pix value (0.0 - 255.0) in double precision given
** a double precision position. Bitmap is assumed to be 8-bit greyscale.
**
** x0,y0 are from bottom corner.
** x0=0.5, y0=0.5 would give exactly the value of the pixel
** in the lower left corner of the bitmap.
*/
double bmp_grey_pix_vald(WILLUSBITMAP *bmp, double x0, double y0)
{
int ix0, iy0, ix1, iy1;
double fx0, fx1, fy0, fy1;
ix0 = (int) (x0 - .5);
ix1 = ix0 + 1;
iy0 = (int) (y0 - .5);
iy1 = iy0 + 1;
BOUND(ix0, 0, bmp->width - 1);
BOUND(ix1, 0, bmp->width - 1);
BOUND(iy0, 0, bmp->height - 1);
BOUND(iy1, 0, bmp->height - 1);
fx0 = 1. - fabs(ix0 + 0.5 - x0);
if (fx0 < 0.)
fx0 = 0.;
fx1 = 1. - fabs(ix1 + 0.5 - x0);
if (fx1 < 0.)
fx1 = 0.;
fy0 = 1. - fabs(iy0 + 0.5 - y0);
if (fy0 < 0.)
fy0 = 0.;
fy1 = 1. - fabs(iy1 + 0.5 - y0);
if (fy1 < 0.)
fy1 = 0.;
if ((fx0 == 0. && fx1 == 0.) || (fy0 == 0. && fy1 == 0.))
return (-1.);
return ((fy0
* (fx0 * bmp_grey_pix_vali(bmp, ix0, iy0)
+ fx1 * bmp_grey_pix_vali(bmp, ix1, iy0))
+ fy1
* (fx0 * bmp_grey_pix_vali(bmp, ix0, iy1)
+ fx1 * bmp_grey_pix_vali(bmp, ix1, iy1)))
/ ((fx0 + fx1) * (fy0 + fy1)));
}
/*
** Return pix values (0.0 - 255.0) in double precision given
** a double precision position.
**
** x0,y0 are from BOTTOM CORNER.
** x0=0.5, y0=0.5 would give exactly the value of the pixel
** in the lower left corner of the bitmap.
*/
static void bmp_pix_vald(WILLUSBITMAP *bmp, double x0, double y0, double *r, double *g,
double *b)
{
int ix0, iy0, ix1, iy1;
double fx0, fx1, fy0, fy1;
int r00, r10, r01, r11;
int g00, g10, g01, g11;
int b00, b10, b01, b11;
ix0 = (int) (x0 - .5);
ix1 = ix0 + 1;
iy0 = (int) (y0 - .5);
iy1 = iy0 + 1;
BOUND(ix0, 0, bmp->width - 1);
BOUND(ix1, 0, bmp->width - 1);
BOUND(iy0, 0, bmp->height - 1);
BOUND(iy1, 0, bmp->height - 1);
fx0 = 1. - fabs(ix0 + 0.5 - x0);
if (fx0 < 0.)
fx0 = 0.;
fx1 = 1. - fabs(ix1 + 0.5 - x0);
if (fx1 < 0.)
fx1 = 0.;
fy0 = 1. - fabs(iy0 + 0.5 - y0);
if (fy0 < 0.)
fy0 = 0.;
fy1 = 1. - fabs(iy1 + 0.5 - y0);
if (fy1 < 0.)
fy1 = 0.;
if ((fx0 == 0. && fx1 == 0.) || (fy0 == 0. && fy1 == 0.)) {
(*r) = (*g) = (*b) = -1.;
return;
}
bmp_pix_vali(bmp, ix0, iy0, &r00, &g00, &b00);
bmp_pix_vali(bmp, ix1, iy0, &r10, &g10, &b10);
bmp_pix_vali(bmp, ix0, iy1, &r01, &g01, &b01);
bmp_pix_vali(bmp, ix1, iy1, &r11, &g11, &b11);
(*r) = ((fy0 * (fx0 * r00 + fx1 * r10) + fy1 * (fx0 * r01 + fx1 * r11))
/ ((fx0 + fx1) * (fy0 + fy1)));
(*g) = ((fy0 * (fx0 * g00 + fx1 * g10) + fy1 * (fx0 * g01 + fx1 * g11))
/ ((fx0 + fx1) * (fy0 + fy1)));
(*b) = ((fy0 * (fx0 * b00 + fx1 * b10) + fy1 * (fx0 * b01 + fx1 * b11))
/ ((fx0 + fx1) * (fy0 + fy1)));
}
static void bmp_rotate_fast(WILLUSBITMAP *bmp, double degrees, int expand)
{
WILLUSBITMAP _dst, *dst;
double th, sth, cth;
int i, r, g, b, w, h, row, col;
dst = &_dst;
th = degrees * PI / 180.;
sth = sin(th);
cth = cos(th);
if (expand) {
w = (int) (fabs(bmp->width * cth) + fabs(bmp->height * sth) + .5);
h = (int) (fabs(bmp->height * cth) + fabs(bmp->width * sth) + .5);
} else {
w = bmp->width;
h = bmp->height;
}
dst = &_dst;
bmp_init(dst);
dst->width = w;
dst->height = h;
dst->bpp = bmp->bpp;
if (dst->bpp == 8)
for (i = 0; i <= 255; i++)
dst->red[i] = dst->green[i] = dst->blue[i] = i;
bmp_alloc(dst);
bmp_pix_vali(bmp, 0, 0, &r, &g, &b);
bmp_fill(dst, r, g, b);
if (dst->bpp == 8)
for (row = 0; row < dst->height; row++) {
unsigned char *p;
double x1, y1, x2, y2;
y2 = dst->height / 2. - row;
p = bmp_rowptr_from_top(dst, row);
for (x2 = -dst->width / 2., col = 0; col < dst->width;
col++, p++, x2 += 1.0) {
double g;
x1 = -.5 + bmp->width / 2. + x2 * cth + y2 * sth;
y1 = -.5 + bmp->height / 2. + y2 * cth - x2 * sth;
if (x1 < 0. || x1 >= bmp->width || y1 < 0. || y1 >= bmp->height)
continue;
g = bmp_grey_pix_vald(bmp, x1, y1);
if (g >= 0.)
p[0] = g;
}
}
else
for (row = 0; row < dst->height; row++) {
unsigned char *p;
double x1, y1, x2, y2;
y2 = dst->height / 2. - row;
p = bmp_rowptr_from_top(dst, row);
for (x2 = -dst->width / 2., col = 0; col < dst->width; col++, p +=
3, x2 += 1.0) {
double rr, gg, bb;
x1 = -.5 + bmp->width / 2. + x2 * cth + y2 * sth;
y1 = -.5 + bmp->height / 2. + y2 * cth - x2 * sth;
if (x1 < 0. || x1 >= bmp->width || y1 < 0. || y1 >= bmp->height)
continue;
bmp_pix_vald(bmp, x1, y1, &rr, &gg, &bb);
if (rr < 0.)
continue;
p[0] = rr;
p[1] = gg;
p[2] = bb;
}
}
bmp_copy(bmp, dst);
bmp_free(dst);
}
static double bmp_autostraighten(WILLUSBITMAP *src, WILLUSBITMAP *srcgrey, int white,
double maxdegrees, double mindegrees, int debug)
{
int i, na, n, imax;
double stepsize, sdmin, sdmax, rotdeg;
double *sdev;
static int rpc = 0;
static char *funcname = "bmp_autostraighten";
rpc++;
stepsize = .5;
na = (int) (maxdegrees / stepsize + .5);
if (na < 1)
na = 1;
n = 1 + na * 2;
sdmin = 999.;
sdmax = -999.;
imax = 0;
willus_mem_alloc_warn((void **) &sdev, n * sizeof(double), funcname, 10);
for (i = 0; i < n; i++) {
double theta, sdev0;
theta = (i - na) * stepsize * PI / 180.;
sdev0 = bmp_row_by_row_stdev(srcgrey, 400, white, theta);
if (sdmin > sdev0)
sdmin = sdev0;
if (sdmax < sdev0) {
imax = i;
sdmax = sdev0;
}
sdev[i] = sdev0;
}
if (sdmax <= 0.) {
willus_mem_free((double **) &sdev, funcname);
return (0.);
}
for (i = 0; i < n; i++)
sdev[i] /= sdmax;
sdmin /= sdmax;
rotdeg = -(imax - na) * stepsize;
if (sdmin > 0.95 || fabs(rotdeg) <= mindegrees
|| fabs(fabs(rotdeg) - fabs(maxdegrees)) < 0.25) {
willus_mem_free((double **) &sdev, funcname);
return (0.);
}
if (imax >= 3 && imax <= n - 4) {
double sd1min, sd2min, sdthresh;
for (sd1min = sdev[imax - 1], i = imax - 2; i >= 0; i--)
if (sd1min > sdev[i])
sd1min = sdev[i];
for (sd2min = sdev[imax + 1], i = imax + 2; i < n; i++)
if (sd2min > sdev[i])
sd2min = sdev[i];
sdthresh = sd1min > sd2min ? sd1min * 1.01 : sd2min * 1.01;
if (sdthresh < 0.9)
sdthresh = 0.9;
if (sdthresh < 0.95) {
double deg1, deg2;
for (i = imax - 1; i >= 0; i--)
if (sdev[i] < sdthresh)
break;
deg1 =
stepsize
* ((i - na)
+ (sdthresh - sdev[i])
/ (sdev[i + 1] - sdev[i]));
for (i = imax + 1; i < n - 1; i++)
if (sdev[i] < sdthresh)
break;
deg2 =
stepsize
* ((i - na)
- (sdthresh - sdev[i])
/ (sdev[i - 1] - sdev[i]));
if (deg2 - deg1 < 2.5) {
rotdeg = -(deg1 + deg2) / 2.;
if (debug)
printf("/sa l \"srcpage %d, %.1f%% line\" 2\n/sa m 2 2\n"
"%g 0\n%g 1\n//nc\n"
"/sa l \"srcpage %d, %.1f%% line\" 2\n/sa m 2 2\n"
"%g 0\n%g 1\n//nc\n", rpc, sdthresh * 100.,
deg1, deg1, rpc, sdthresh * 100., deg2, deg2);
}
}
}
printf("\n(Straightening page: rotating cc by %.2f deg.)\n", rotdeg);
/* BMP rotation fills with pixel value at (0,0) */
srcgrey->data[0] = 255;
bmp_rotate_fast(srcgrey, rotdeg, 0);
if (src != NULL) {
src->data[0] = src->data[1] = src->data[2] = 255;
bmp_rotate_fast(src, rotdeg, 0);
}
willus_mem_free((double **) &sdev, funcname);
return (rotdeg);
}
/* bmpmupdf.c */
static int bmpmupdf_pixmap_to_bmp(WILLUSBITMAP *bmp, fz_context *ctx,
fz_pixmap *pixmap)

View File

@@ -29,7 +29,7 @@
void k2pdfopt_set_params(int bb_width, int bb_height, \
double font_size, double page_margin, \
double line_space, double word_space, \
int wrapping, int justification, \
int wrapping, int straighten, int justification, \
int columns, double contrast);
void k2pdfopt_mupdf_reflow(fz_document *doc, fz_page *page, fz_context *ctx);
void k2pdfopt_djvu_reflow(ddjvu_page_t *page, ddjvu_context_t *ctx, ddjvu_render_mode_t mode, ddjvu_format_t *fmt);

9
pdf.c
View File

@@ -522,12 +522,13 @@ static int reflowPage(lua_State *L) {
double line_spacing = luaL_checknumber(L, 8);
double word_spacing = luaL_checknumber(L, 9);
int text_wrap = luaL_checkint(L, 10);
int justification = luaL_checkint(L, 11);
int columns = luaL_checkint(L, 12);
double contrast = luaL_checknumber(L, 13);
int straighten = luaL_checkint(L, 11);
int justification = luaL_checkint(L, 12);
int columns = luaL_checkint(L, 13);
double contrast = luaL_checknumber(L, 14);
k2pdfopt_set_params(width, height, font_size, page_margin, line_spacing, word_spacing, \
text_wrap, justification, columns, contrast);
text_wrap, straighten, justification, columns, contrast);
k2pdfopt_mupdf_reflow(page->doc->xref, page->page, page->doc->context);
k2pdfopt_rfbmp_size(&width, &height);
k2pdfopt_rfbmp_zoom(&dc->zoom);