Merge branch 'feature/leptdeskew' into feature/logging

Need leptonica for testing now, I think
# Conflicts:
#	ocrmypdf/tesseract.py
#	requirements.txt
#	setup.py
This commit is contained in:
James R. Barlow
2016-02-08 12:34:48 -08:00
5 changed files with 95 additions and 86 deletions
+21 -83
View File
@@ -10,11 +10,14 @@
from __future__ import print_function, absolute_import, division
import argparse
import ctypes as C
import sys
import os
import logging
from tempfile import TemporaryFile
from ctypes.util import find_library
from .lib._leptonica import ffi
lept = ffi.dlopen(find_library('lept'))
logger = logging.getLogger(__name__)
@@ -25,67 +28,6 @@ def stderr(*objs):
print("leptonica.py:", *objs, file=sys.stderr)
from ctypes.util import find_library
lept_lib = find_library('lept')
if not lept_lib:
stderr("Could not find the Leptonica library")
sys.exit(3)
try:
lept = C.cdll.LoadLibrary(lept_lib)
except Exception:
stderr("Could not load the Leptonica library from %s", lept_lib)
sys.exit(3)
class _PIXCOLORMAP(C.Structure):
"""struct PixColormap from Leptonica src/pix.h
"""
_fields_ = [
("array", C.c_void_p),
("depth", C.c_int32),
("nalloc", C.c_int32),
("n", C.c_int32)
]
class _PIX(C.Structure):
"""struct Pix from Leptonica src/pix.h
"""
_fields_ = [
("w", C.c_uint32),
("h", C.c_uint32),
("d", C.c_uint32),
("wpl", C.c_uint32),
("refcount", C.c_uint32),
("xres", C.c_int32),
("yres", C.c_int32),
("informat", C.c_int32),
("text", C.POINTER(C.c_char)),
("colormap", C.POINTER(_PIXCOLORMAP)),
("data", C.POINTER(C.c_uint32))
]
PIX = C.POINTER(_PIX)
lept.pixRead.argtypes = [C.c_char_p]
lept.pixRead.restype = PIX
lept.pixScale.argtypes = [PIX, C.c_float, C.c_float]
lept.pixScale.restype = PIX
lept.pixDeskew.argtypes = [PIX, C.c_int32]
lept.pixDeskew.restype = PIX
lept.pixFindSkew.argtypes = [PIX, C.POINTER(C.c_float), C.POINTER(C.c_float)]
lept.pixFindSkew.restype = C.c_int32
lept.pixWriteImpliedFormat.argtypes = [C.c_char_p, PIX, C.c_int32, C.c_int32]
lept.pixWriteImpliedFormat.restype = C.c_int32
lept.pixDestroy.argtypes = [C.POINTER(PIX)]
lept.pixDestroy.restype = None
lept.getLeptonicaVersion.argtypes = []
lept.getLeptonicaVersion.restype = C.c_char_p
class LeptonicaErrorTrap(object):
"""Context manager to trap errors reported by Leptonica.
@@ -140,6 +82,12 @@ class LeptonicaIOError(LeptonicaError):
pass
def _pix_destroy(pix):
ptr_to_pix = ffi.new('PIX **', pix)
lept.pixDestroy(ptr_to_pix)
print('pix destroy ' + repr(pix))
def pixRead(filename):
"""Load an image file into a PIX object.
@@ -148,7 +96,8 @@ def pixRead(filename):
"""
with LeptonicaErrorTrap():
return lept.pixRead(filename.encode(sys.getfilesystemencoding()))
pix = lept.pixRead(filename.encode(sys.getfilesystemencoding()))
return ffi.gc(pix, _pix_destroy)
def pixScale(pix, scalex, scaley):
@@ -168,7 +117,8 @@ def pixDeskew(pix, reduction_factor=0):
"""
with LeptonicaErrorTrap():
return lept.pixDeskew(pix, reduction_factor)
deskewed = lept.pixDeskew(pix, reduction_factor)
return ffi.gc(deskewed, _pix_destroy)
def pixFindSkew(pix):
@@ -178,11 +128,11 @@ def pixFindSkew(pix):
"""
with LeptonicaErrorTrap():
angle = C.c_float(0.0)
confidence = C.c_float(0.0)
result = lept.pixFindSkew(pix, C.byref(angle), C.byref(confidence))
angle = ffi.new('float *', 0.0)
confidence = ffi.new('float *', 0.0)
result = lept.pixFindSkew(pix, angle, confidence)
if result == 0:
return (angle.value, confidence.value)
return (angle[0], confidence[0])
else:
return (None, None)
@@ -212,17 +162,6 @@ def pixWriteImpliedFormat(filename, pix, jpeg_quality=0, jpeg_progressive=0):
move(filename, filename[:-4]) # Remove .pnm suffix
def pixDestroy(pix):
"""Destroy the pix object.
Function signature is pixDestroy(struct Pix **), hence C.byref() to pass
the address of the pointer.
"""
with LeptonicaErrorTrap():
lept.pixDestroy(C.byref(pix))
def getLeptonicaVersion():
"""Get Leptonica version string.
@@ -231,12 +170,13 @@ def getLeptonicaVersion():
a pointless effort to reclaim 100 bytes of memory.
"""
return lept.getLeptonicaVersion().decode()
return ffi.string(lept.getLeptonicaVersion()).decode()
def deskew(infile, outfile, dpi):
try:
pix_source = pixRead(infile)
print(repr(pix_source))
except LeptonicaIOError:
raise LeptonicaIOError("Failed to open file: %s" % infile)
@@ -245,13 +185,12 @@ def deskew(infile, outfile, dpi):
else:
reduction_factor = 0 # Use default
pix_deskewed = pixDeskew(pix_source, reduction_factor)
print(repr(pix_deskewed))
try:
pixWriteImpliedFormat(outfile, pix_deskewed)
except LeptonicaIOError:
raise LeptonicaIOError("Failed to open destination file: %s" % outfile)
pixDestroy(pix_source)
pixDestroy(pix_deskewed)
if __name__ == '__main__':
@@ -325,7 +264,6 @@ def test_skew_angle():
rotated_im.save(tmpfile)
pix = pixRead(tmpfile.name)
angle, confidence = pixFindSkew(pix)
pixDestroy(pix)
print('{0} {1} {2}'.format(rotate_angle, angle, confidence), file=sys.stderr)
+58
View File
@@ -0,0 +1,58 @@
from cffi import FFI
ffi = FFI()
ffi.set_source("ocrmypdf.lib._leptonica", None)
ffi.cdef("""
typedef signed char l_int8;
typedef unsigned char l_uint8;
typedef short l_int16;
typedef unsigned short l_uint16;
typedef int l_int32;
typedef unsigned int l_uint32;
typedef float l_float32;
typedef double l_float64;
typedef long long l_int64;
typedef unsigned long long l_uint64;
struct Pix
{
l_uint32 w; /* width in pixels */
l_uint32 h; /* height in pixels */
l_uint32 d; /* depth in bits (bpp) */
l_uint32 spp; /* number of samples per pixel */
l_uint32 wpl; /* 32-bit words/line */
l_uint32 refcount; /* reference count (1 if no clones) */
l_int32 xres; /* image res (ppi) in x direction */
/* (use 0 if unknown) */
l_int32 yres; /* image res (ppi) in y direction */
/* (use 0 if unknown) */
l_int32 informat; /* input file format, IFF_* */
l_int32 special; /* special instructions for I/O, etc */
char *text; /* text string associated with pix */
struct PixColormap *colormap; /* colormap (may be null) */
l_uint32 *data; /* the image data */
};
typedef struct Pix PIX;
struct PixColormap
{
void *array; /* colormap table (array of RGBA_QUAD) */
l_int32 depth; /* of pix (1, 2, 4 or 8 bpp) */
l_int32 nalloc; /* number of color entries allocated */
l_int32 n; /* number of color entries used */
};
typedef struct PixColormap PIXCMAP;
""")
ffi.cdef("""
PIX * pixRead ( const char *filename );
PIX * pixScale ( PIX *pixs, l_float32 scalex, l_float32 scaley );
l_int32 pixFindSkew ( PIX *pixs, l_float32 *pangle, l_float32 *pconf );
l_int32 pixWriteImpliedFormat ( const char *filename, PIX *pix, l_int32 quality, l_int32 progressive );
void pixDestroy ( PIX **ppix );
PIX * pixDeskew ( PIX *pixs, l_int32 redsearch );
char * getLeptonicaVersion ( );
""")
if __name__ == '__main__':
ffi.compile()
+8 -1
View File
@@ -187,6 +187,9 @@ advanced.add_argument(
'--tesseract-timeout', default=180.0, type=float, metavar='SECONDS',
help='give up on OCR after the timeout, but copy the preprocessed page '
'into the final output')
advanced.add_argument(
'--deskewer', choices=['leptonica', 'unpaper'], default='leptonica',
help='choose deskew provider')
debugging = parser.add_argument_group(
"Debugging",
@@ -585,7 +588,11 @@ def preprocess_deskew(
pageinfo = get_pageinfo(input_file, pdfinfo, pdfinfo_lock)
dpi = int(pageinfo['xres'])
unpaper.deskew(input_file, output_file, dpi, log)
if options.deskewer == 'unpaper':
unpaper.deskew(input_file, output_file, dpi, log)
else:
from . import leptonica
leptonica.deskew(input_file, output_file, dpi)
@transform(
+1
View File
@@ -3,3 +3,4 @@ Pillow==3.1.1
reportlab==3.2.0
PyPDF2==1.25.1
img2pdf==0.2
cffi>=1.5.0
+7 -2
View File
@@ -208,15 +208,20 @@ setup(
"Topic :: Text Processing :: Linguistic",
],
setup_requires=[
'setuptools_scm'
'setuptools_scm',
'cffi>=1.0.0'
],
use_scm_version={'version_scheme': 'post-release'},
cffi_modules=[
'ocrmypdf/lib/compile_leptonica.py:ffi'
],
install_requires=[
'ruffus>=2.6.3',
'Pillow>=2.4.0',
'reportlab>=3.1.44',
'PyPDF2>=1.25.1',
'img2pdf>=0.2.0'
'img2pdf>=0.2.0',
'cffi>=1.5.0'
],
tests_require=tests_require,
entry_points={