2
* The Python Imaging Library
4
* A binary morphology add-on for the Python Imaging Library
7
* 2014-06-04 Initial version.
9
* Copyright (c) 2014 Dov Grobgeld <dov.grobgeld@gmail.com>
11
* See the README file for information on usage and redistribution.
15
#include "libImaging/Imaging.h"
17
#define LUT_SIZE (1 << 9)
19
/* Apply a morphologic LUT to a binary image. Outputs a
24
1. a LUT - a 512 byte size lookup table.
25
2. an input Imaging image id.
26
3. an output Imaging image id
28
Returns number of changed pixels.
31
apply(PyObject *self, PyObject *args) {
34
Py_ssize_t lut_len, i0, i1;
35
Imaging imgin, imgout;
38
UINT8 **inrows, **outrows;
39
int num_changed_pixels = 0;
41
if (!PyArg_ParseTuple(args, "Onn", &py_lut, &i0, &i1)) {
42
PyErr_SetString(PyExc_RuntimeError, "Argument parsing problem");
46
if (!PyBytes_Check(py_lut)) {
47
PyErr_SetString(PyExc_RuntimeError, "The morphology LUT is not a bytes object");
51
lut_len = PyBytes_Size(py_lut);
53
if (lut_len < LUT_SIZE) {
54
PyErr_SetString(PyExc_RuntimeError, "The morphology LUT has the wrong size");
58
lut = PyBytes_AsString(py_lut);
63
height = imgin->ysize;
65
if (imgin->type != IMAGING_TYPE_UINT8 || imgin->bands != 1) {
66
PyErr_SetString(PyExc_RuntimeError, "Unsupported image type");
69
if (imgout->type != IMAGING_TYPE_UINT8 || imgout->bands != 1) {
70
PyErr_SetString(PyExc_RuntimeError, "Unsupported image type");
74
inrows = imgin->image8;
75
outrows = imgout->image8;
77
for (row_idx = 0; row_idx < height; row_idx++) {
78
UINT8 *outrow = outrows[row_idx];
79
UINT8 *inrow = inrows[row_idx];
80
UINT8 *prow, *nrow; /* Previous and next row */
82
/* zero boundary conditions. TBD support other modes */
83
outrow[0] = outrow[width - 1] = 0;
84
if (row_idx == 0 || row_idx == height - 1) {
85
for (col_idx = 0; col_idx < width; col_idx++) {
91
prow = inrows[row_idx - 1];
92
nrow = inrows[row_idx + 1];
94
for (col_idx = 1; col_idx < width - 1; col_idx++) {
95
int cim = col_idx - 1;
96
int cip = col_idx + 1;
97
unsigned char b0 = prow[cim] & 1;
98
unsigned char b1 = prow[col_idx] & 1;
99
unsigned char b2 = prow[cip] & 1;
101
unsigned char b3 = inrow[cim] & 1;
102
unsigned char b4 = inrow[col_idx] & 1;
103
unsigned char b5 = inrow[cip] & 1;
105
unsigned char b6 = nrow[cim] & 1;
106
unsigned char b7 = nrow[col_idx] & 1;
107
unsigned char b8 = nrow[cip] & 1;
110
(b0 | (b1 << 1) | (b2 << 2) | (b3 << 3) | (b4 << 4) | (b5 << 5) |
111
(b6 << 6) | (b7 << 7) | (b8 << 8));
112
outrow[col_idx] = 255 * (lut[lut_idx] & 1);
113
num_changed_pixels += ((b4 & 1) != (outrow[col_idx] & 1));
116
return Py_BuildValue("i", num_changed_pixels);
119
/* Match a morphologic LUT to a binary image and return a list
120
of the coordinates of all matching pixels.
124
1. a LUT - a 512 byte size lookup table.
125
2. an input Imaging image id.
127
Returns list of matching pixels.
130
match(PyObject *self, PyObject *args) {
133
Py_ssize_t lut_len, i0;
136
int row_idx, col_idx;
138
PyObject *ret = PyList_New(0);
143
if (!PyArg_ParseTuple(args, "On", &py_lut, &i0)) {
145
PyErr_SetString(PyExc_RuntimeError, "Argument parsing problem");
149
if (!PyBytes_Check(py_lut)) {
151
PyErr_SetString(PyExc_RuntimeError, "The morphology LUT is not a bytes object");
155
lut_len = PyBytes_Size(py_lut);
157
if (lut_len < LUT_SIZE) {
159
PyErr_SetString(PyExc_RuntimeError, "The morphology LUT has the wrong size");
163
lut = PyBytes_AsString(py_lut);
166
if (imgin->type != IMAGING_TYPE_UINT8 || imgin->bands != 1) {
168
PyErr_SetString(PyExc_RuntimeError, "Unsupported image type");
172
inrows = imgin->image8;
173
width = imgin->xsize;
174
height = imgin->ysize;
176
for (row_idx = 1; row_idx < height - 1; row_idx++) {
177
UINT8 *inrow = inrows[row_idx];
180
prow = inrows[row_idx - 1];
181
nrow = inrows[row_idx + 1];
183
for (col_idx = 1; col_idx < width - 1; col_idx++) {
184
int cim = col_idx - 1;
185
int cip = col_idx + 1;
186
unsigned char b0 = prow[cim] & 1;
187
unsigned char b1 = prow[col_idx] & 1;
188
unsigned char b2 = prow[cip] & 1;
190
unsigned char b3 = inrow[cim] & 1;
191
unsigned char b4 = inrow[col_idx] & 1;
192
unsigned char b5 = inrow[cip] & 1;
194
unsigned char b6 = nrow[cim] & 1;
195
unsigned char b7 = nrow[col_idx] & 1;
196
unsigned char b8 = nrow[cip] & 1;
199
(b0 | (b1 << 1) | (b2 << 2) | (b3 << 3) | (b4 << 4) | (b5 << 5) |
200
(b6 << 6) | (b7 << 7) | (b8 << 8));
202
PyObject *coordObj = Py_BuildValue("(nn)", col_idx, row_idx);
203
PyList_Append(ret, coordObj);
204
Py_XDECREF(coordObj);
212
/* Return a list of the coordinates of all turned on pixels in an image.
213
May be used to extract features after a sequence of MorphOps were applied.
214
This is faster than match as only 1x1 lookup is made.
217
get_on_pixels(PyObject *self, PyObject *args) {
221
int row_idx, col_idx;
223
PyObject *ret = PyList_New(0);
228
if (!PyArg_ParseTuple(args, "n", &i0)) {
230
PyErr_SetString(PyExc_RuntimeError, "Argument parsing problem");
238
for (row_idx = 0; row_idx < height; row_idx++) {
239
UINT8 *row = rows[row_idx];
240
for (col_idx = 0; col_idx < width; col_idx++) {
242
PyObject *coordObj = Py_BuildValue("(nn)", col_idx, row_idx);
243
PyList_Append(ret, coordObj);
244
Py_XDECREF(coordObj);
251
static PyMethodDef functions[] = {
253
{"apply", (PyCFunction)apply, METH_VARARGS, NULL},
254
{"get_on_pixels", (PyCFunction)get_on_pixels, METH_VARARGS, NULL},
255
{"match", (PyCFunction)match, METH_VARARGS, NULL},
256
{NULL, NULL, 0, NULL}
260
PyInit__imagingmorph(void) {
263
static PyModuleDef module_def = {
264
PyModuleDef_HEAD_INIT,
265
"_imagingmorph", /* m_name */
266
"A module for doing image morphology", /* m_doc */
268
functions, /* m_methods */
271
m = PyModule_Create(&module_def);
273
#ifdef Py_GIL_DISABLED
274
PyUnstable_Module_SetGIL(m, Py_MOD_GIL_NOT_USED);