Skip to content

Commit 46839b7

Browse files
committed
Support image normalization
1 parent 2987090 commit 46839b7

File tree

4 files changed

+331
-12
lines changed

4 files changed

+331
-12
lines changed

docscanner/__init__.py

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,31 @@
33
import json
44
__version__ = version
55

6-
6+
class ImagePixelFormat:
7+
# 0:Black, 1:White
8+
IPF_BINARY = 0
9+
# 0:White, 1:Black
10+
IPF_BINARYINVERTED = 1
11+
# 8bit gray
12+
IPF_GRAYSCALED = 2
13+
# NV21
14+
IPF_NV21 = 3
15+
# 16bit with RGB channel order stored in memory from high to low address
16+
IPF_RGB_565 = 4
17+
# 16bit with RGB channel order stored in memory from high to low address
18+
IPF_RGB_555 = 5
19+
# 24bit with RGB channel order stored in memory from high to low address
20+
IPF_RGB_888 = 6
21+
# 32bit with ARGB channel order stored in memory from high to low address
22+
IPF_ARGB_8888 = 7
23+
# 48bit with RGB channel order stored in memory from high to low address
24+
IPF_RGB_161616 = 8
25+
# 64bit with ARGB channel order stored in memory from high to low address
26+
IPF_ARGB_16161616 = 9
27+
# 32bit with ABGR channel order stored in memory from high to low address
28+
IPF_ABGR_8888 = 10
29+
# 64bit with ABGR channel order stored in memory from high to low address
30+
IPF_ABGR_16161616 = 11
31+
# 24bit with BGR channel order stored in memory from high to low address
32+
IPF_BGR_888 = 12
33+

src/document_scanner.h

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include <structmember.h>
66
#include "DynamsoftDocumentNormalizer.h"
77
#include "document_result.h"
8+
#include "normalized_image.h"
89
#include <thread>
910
#include <condition_variable>
1011
#include <mutex>
@@ -399,12 +400,149 @@ static PyObject *setParameters(PyObject *obj, PyObject *args)
399400
return Py_BuildValue("i", ret);
400401
}
401402

403+
PyObject *createNormalizedImage(ImageData *imageData)
404+
{
405+
NormalizedImage *ni = PyObject_New(NormalizedImage, &NormalizedImageType);
406+
ni->bytearray = PyByteArray_FromStringAndSize((const char *)imageData->bytes, imageData->bytesLength);
407+
ni->length = Py_BuildValue("i", imageData->bytesLength);
408+
ni->width = Py_BuildValue("i", imageData->width);
409+
ni->height = Py_BuildValue("i", imageData->height);
410+
ni->stride = Py_BuildValue("i", imageData->stride);
411+
ni->format = Py_BuildValue("i", imageData->format);
412+
return (PyObject *)ni;
413+
}
414+
/**
415+
* Normalize the document.
416+
*
417+
* @param string filePath
418+
*
419+
* @return Normalized document image
420+
*/
421+
static PyObject *normalizeFile(PyObject *obj, PyObject *args)
422+
{
423+
DynamsoftDocumentScanner *self = (DynamsoftDocumentScanner *)obj;
424+
425+
char *pFileName;
426+
int x1, y1, x2, y2, x3, y3, x4, y4;
427+
if (!PyArg_ParseTuple(args, "siiiiiiii", &pFileName, &x1, &y1, &x2, &y2, &x3, &y3, &x4, &y4))
428+
return NULL;
429+
430+
Quadrilateral quad;
431+
quad.points[0].coordinate[0] = x1;
432+
quad.points[0].coordinate[1] = y1;
433+
quad.points[1].coordinate[0] = x2;
434+
quad.points[1].coordinate[1] = y2;
435+
quad.points[2].coordinate[0] = x3;
436+
quad.points[2].coordinate[1] = y3;
437+
quad.points[3].coordinate[0] = x4;
438+
quad.points[3].coordinate[1] = y4;
439+
440+
NormalizedImageResult* normalizedResult = NULL;
441+
int errorCode = DDN_NormalizeFile(self->handler, pFileName, "", &quad, &normalizedResult);
442+
if (errorCode != DM_OK)
443+
printf("%s\r\n", DC_GetErrorString(errorCode));
444+
445+
ImageData *imageData = normalizedResult->image;
446+
447+
PyObject *normalizedImage = createNormalizedImage(imageData);
448+
449+
if (normalizedResult != NULL)
450+
DDN_FreeNormalizedImageResult(&normalizedResult);
451+
452+
return normalizedImage;
453+
}
454+
455+
/**
456+
* Normalize the document.
457+
*
458+
* @param Mat image
459+
*
460+
* @return Normalized document image
461+
*/
462+
static PyObject *normalizeBuffer(PyObject *obj, PyObject *args)
463+
{
464+
DynamsoftDocumentScanner *self = (DynamsoftDocumentScanner *)obj;
465+
466+
PyObject *o;
467+
int x1, y1, x2, y2, x3, y3, x4, y4;
468+
if (!PyArg_ParseTuple(args, "Oiiiiiiii", &o, &x1, &y1, &x2, &y2, &x3, &y3, &x4, &y4))
469+
return NULL;
470+
471+
Py_buffer *view;
472+
int nd;
473+
PyObject *memoryview = PyMemoryView_FromObject(o);
474+
if (memoryview == NULL)
475+
{
476+
PyErr_Clear();
477+
return NULL;
478+
}
479+
480+
view = PyMemoryView_GET_BUFFER(memoryview);
481+
char *buffer = (char *)view->buf;
482+
nd = view->ndim;
483+
int len = view->len;
484+
int stride = view->strides[0];
485+
int width = view->strides[0] / view->strides[1];
486+
int height = len / stride;
487+
488+
ImagePixelFormat format = IPF_RGB_888;
489+
490+
if (width == stride)
491+
{
492+
format = IPF_GRAYSCALED;
493+
}
494+
else if (width * 3 == stride)
495+
{
496+
format = IPF_RGB_888;
497+
}
498+
else if (width * 4 == stride)
499+
{
500+
format = IPF_ARGB_8888;
501+
}
502+
503+
ImageData data;
504+
data.bytes = (unsigned char *)buffer;
505+
data.width = width;
506+
data.height = height;
507+
data.stride = stride;
508+
data.format = format;
509+
data.bytesLength = len;
510+
511+
Quadrilateral quad;
512+
quad.points[0].coordinate[0] = x1;
513+
quad.points[0].coordinate[1] = y1;
514+
quad.points[1].coordinate[0] = x2;
515+
quad.points[1].coordinate[1] = y2;
516+
quad.points[2].coordinate[0] = x3;
517+
quad.points[2].coordinate[1] = y3;
518+
quad.points[3].coordinate[0] = x4;
519+
quad.points[3].coordinate[1] = y4;
520+
521+
NormalizedImageResult* normalizedResult = NULL;
522+
int errorCode = DDN_NormalizeBuffer(self->handler, &data, "", &quad, &normalizedResult);
523+
if (errorCode != DM_OK)
524+
printf("%s\r\n", DC_GetErrorString(errorCode));
525+
526+
ImageData *imageData = normalizedResult->image;
527+
528+
PyObject *normalizedImage = createNormalizedImage(imageData);
529+
530+
if (normalizedResult != NULL)
531+
DDN_FreeNormalizedImageResult(&normalizedResult);
532+
533+
Py_DECREF(memoryview);
534+
535+
return normalizedImage;
536+
}
537+
402538
static PyMethodDef instance_methods[] = {
403539
{"decodeFile", decodeFile, METH_VARARGS, NULL},
404540
{"decodeMat", decodeMat, METH_VARARGS, NULL},
405541
{"addAsyncListener", addAsyncListener, METH_VARARGS, NULL},
406542
{"decodeMatAsync", decodeMatAsync, METH_VARARGS, NULL},
407543
{"setParameters", setParameters, METH_VARARGS, NULL},
544+
{"normalizeFile", normalizeFile, METH_VARARGS, NULL},
545+
{"normalizeBuffer", normalizeBuffer, METH_VARARGS, NULL},
408546
{NULL, NULL, 0, NULL}
409547
};
410548

src/normalized_image.h

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
#ifndef __NORMALIZED_IMAGE_H__
2+
#define __NORMALIZED_IMAGE_H__
3+
4+
#include <Python.h>
5+
#include <structmember.h>
6+
7+
// https://docs.python.org/3/c-api/typeobj.html#typedef-examples
8+
typedef struct
9+
{
10+
PyObject_HEAD
11+
PyObject *bytearray;
12+
PyObject *length;
13+
PyObject *width;
14+
PyObject *height;
15+
PyObject *stride;
16+
PyObject *format;
17+
} NormalizedImage;
18+
19+
static void NormalizedImage_dealloc(NormalizedImage *self)
20+
{
21+
if (self->bytearray) Py_DECREF(self->bytearray);
22+
if (self->length) Py_DECREF(self->length);
23+
if (self->width) Py_DECREF(self->width);
24+
if (self->height) Py_DECREF(self->height);
25+
if (self->stride) Py_DECREF(self->stride);
26+
if (self->format) Py_DECREF(self->format);
27+
Py_TYPE(self)->tp_free((PyObject *)self);
28+
}
29+
30+
static PyObject *NormalizedImage_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
31+
{
32+
NormalizedImage *self;
33+
34+
self = (NormalizedImage *)type->tp_alloc(type, 0);
35+
return (PyObject *)self;
36+
}
37+
38+
static PyMemberDef NormalizedImage_members[] = {
39+
{"bytearray", T_OBJECT_EX, offsetof(NormalizedImage, bytearray), 0, "bytearray"},
40+
{"length", T_OBJECT_EX, offsetof(NormalizedImage, length), 0, "length"},
41+
{"width", T_OBJECT_EX, offsetof(NormalizedImage, width), 0, "width"},
42+
{"height", T_OBJECT_EX, offsetof(NormalizedImage, height), 0, "height"},
43+
{"stride", T_OBJECT_EX, offsetof(NormalizedImage, stride), 0, "stride"},
44+
{"format", T_OBJECT_EX, offsetof(NormalizedImage, format), 0, "format"},
45+
{NULL} /* Sentinel */
46+
};
47+
48+
static PyTypeObject NormalizedImageType = {
49+
PyVarObject_HEAD_INIT(NULL, 0) "docscanner.NormalizedImage", /* tp_name */
50+
sizeof(NormalizedImage), /* tp_basicsize */
51+
0, /* tp_itemsize */
52+
(destructor)NormalizedImage_dealloc, /* tp_dealloc */
53+
0, /* tp_print */
54+
0, /* tp_getattr */
55+
0, /* tp_setattr */
56+
0, /* tp_reserved */
57+
0, /* tp_repr */
58+
0, /* tp_as_number */
59+
0, /* tp_as_sequence */
60+
0, /* tp_as_mapping */
61+
0, /* tp_hash */
62+
0, /* tp_call */
63+
0, /* tp_str */
64+
PyObject_GenericGetAttr, /* tp_getattro */
65+
PyObject_GenericSetAttr, /* tp_setattro */
66+
0, /* tp_as_buffer */
67+
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
68+
"NormalizedImage", /* tp_doc */
69+
0, /* tp_traverse */
70+
0, /* tp_clear */
71+
0, /* tp_richcompare */
72+
0, /* tp_weaklistoffset */
73+
0, /* tp_iter */
74+
0, /* tp_iternext */
75+
0, /* tp_methods */
76+
NormalizedImage_members, /* tp_members */
77+
0, /* tp_getset */
78+
0, /* tp_base */
79+
0, /* tp_dict */
80+
0, /* tp_descr_get */
81+
0, /* tp_descr_set */
82+
0, /* tp_dictoffset */
83+
0, /* tp_init */
84+
0, /* tp_alloc */
85+
NormalizedImage_new, /* tp_new */
86+
};
87+
88+
#endif

0 commit comments

Comments
 (0)