2014-06-05 00:03:00 +04:00
|
|
|
/*
|
|
|
|
* The Python Imaging Library
|
|
|
|
*
|
|
|
|
* A binary morphology add-on for the Python Imaging Library
|
|
|
|
*
|
|
|
|
* History:
|
|
|
|
* 2014-06-04 Initial version.
|
|
|
|
*
|
|
|
|
* Copyright (c) 2014 Dov Grobgeld <dov.grobgeld@gmail.com>
|
|
|
|
*
|
|
|
|
* See the README file for information on usage and redistribution.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "Python.h"
|
2020-09-16 08:10:28 +03:00
|
|
|
#include "libImaging/Imaging.h"
|
2014-06-05 00:03:00 +04:00
|
|
|
|
2021-01-03 06:17:51 +03:00
|
|
|
#define LUT_SIZE (1 << 9)
|
2014-06-05 00:03:00 +04:00
|
|
|
|
|
|
|
/* Apply a morphologic LUT to a binary image. Outputs a
|
|
|
|
a new binary image.
|
|
|
|
|
|
|
|
Expected parameters:
|
|
|
|
|
|
|
|
1. a LUT - a 512 byte size lookup table.
|
|
|
|
2. an input Imaging image id.
|
|
|
|
3. an output Imaging image id
|
|
|
|
|
|
|
|
Returns number of changed pixels.
|
|
|
|
*/
|
2021-01-03 06:17:51 +03:00
|
|
|
static PyObject *
|
|
|
|
apply(PyObject *self, PyObject *args) {
|
2014-06-05 00:03:00 +04:00
|
|
|
const char *lut;
|
2014-06-20 01:54:39 +04:00
|
|
|
PyObject *py_lut;
|
2014-06-05 00:03:00 +04:00
|
|
|
Py_ssize_t lut_len, i0, i1;
|
|
|
|
Imaging imgin, imgout;
|
|
|
|
int width, height;
|
|
|
|
int row_idx, col_idx;
|
|
|
|
UINT8 **inrows, **outrows;
|
|
|
|
int num_changed_pixels = 0;
|
|
|
|
|
2014-06-20 01:54:39 +04:00
|
|
|
if (!PyArg_ParseTuple(args, "Onn", &py_lut, &i0, &i1)) {
|
2014-06-05 00:03:00 +04:00
|
|
|
PyErr_SetString(PyExc_RuntimeError, "Argument parsing problem");
|
2014-06-20 01:54:39 +04:00
|
|
|
return NULL;
|
|
|
|
}
|
2014-06-05 00:03:00 +04:00
|
|
|
|
2014-06-24 03:02:29 +04:00
|
|
|
if (!PyBytes_Check(py_lut)) {
|
|
|
|
PyErr_SetString(PyExc_RuntimeError, "The morphology LUT is not a bytes object");
|
2014-06-05 00:03:00 +04:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2014-06-24 03:02:29 +04:00
|
|
|
lut_len = PyBytes_Size(py_lut);
|
2014-06-20 01:54:39 +04:00
|
|
|
|
2014-06-05 00:03:00 +04:00
|
|
|
if (lut_len < LUT_SIZE) {
|
|
|
|
PyErr_SetString(PyExc_RuntimeError, "The morphology LUT has the wrong size");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2014-06-24 03:02:29 +04:00
|
|
|
lut = PyBytes_AsString(py_lut);
|
2014-06-20 01:54:39 +04:00
|
|
|
|
2021-01-03 06:17:51 +03:00
|
|
|
imgin = (Imaging)i0;
|
|
|
|
imgout = (Imaging)i1;
|
2014-06-05 00:03:00 +04:00
|
|
|
width = imgin->xsize;
|
|
|
|
height = imgin->ysize;
|
|
|
|
|
2021-01-03 06:17:51 +03:00
|
|
|
if (imgin->type != IMAGING_TYPE_UINT8 || imgin->bands != 1) {
|
2018-04-22 20:52:38 +03:00
|
|
|
PyErr_SetString(PyExc_RuntimeError, "Unsupported image type");
|
2014-06-05 00:03:00 +04:00
|
|
|
return NULL;
|
|
|
|
}
|
2021-01-03 06:17:51 +03:00
|
|
|
if (imgout->type != IMAGING_TYPE_UINT8 || imgout->bands != 1) {
|
2018-04-22 20:52:38 +03:00
|
|
|
PyErr_SetString(PyExc_RuntimeError, "Unsupported image type");
|
2014-06-05 00:03:00 +04:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
inrows = imgin->image8;
|
|
|
|
outrows = imgout->image8;
|
|
|
|
|
2021-01-03 06:17:51 +03:00
|
|
|
for (row_idx = 0; row_idx < height; row_idx++) {
|
2014-06-05 00:03:00 +04:00
|
|
|
UINT8 *outrow = outrows[row_idx];
|
|
|
|
UINT8 *inrow = inrows[row_idx];
|
|
|
|
UINT8 *prow, *nrow; /* Previous and next row */
|
|
|
|
|
|
|
|
/* zero boundary conditions. TBD support other modes */
|
2021-01-03 06:17:51 +03:00
|
|
|
outrow[0] = outrow[width - 1] = 0;
|
|
|
|
if (row_idx == 0 || row_idx == height - 1) {
|
|
|
|
for (col_idx = 0; col_idx < width; col_idx++) {
|
2014-06-05 00:03:00 +04:00
|
|
|
outrow[col_idx] = 0;
|
2020-05-10 12:56:36 +03:00
|
|
|
}
|
2014-06-05 00:03:00 +04:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2021-01-03 06:17:51 +03:00
|
|
|
prow = inrows[row_idx - 1];
|
|
|
|
nrow = inrows[row_idx + 1];
|
|
|
|
|
|
|
|
for (col_idx = 1; col_idx < width - 1; col_idx++) {
|
|
|
|
int cim = col_idx - 1;
|
|
|
|
int cip = col_idx + 1;
|
|
|
|
unsigned char b0 = prow[cim] & 1;
|
|
|
|
unsigned char b1 = prow[col_idx] & 1;
|
|
|
|
unsigned char b2 = prow[cip] & 1;
|
|
|
|
|
|
|
|
unsigned char b3 = inrow[cim] & 1;
|
|
|
|
unsigned char b4 = inrow[col_idx] & 1;
|
|
|
|
unsigned char b5 = inrow[cip] & 1;
|
|
|
|
|
|
|
|
unsigned char b6 = nrow[cim] & 1;
|
|
|
|
unsigned char b7 = nrow[col_idx] & 1;
|
|
|
|
unsigned char b8 = nrow[cip] & 1;
|
|
|
|
|
|
|
|
int lut_idx =
|
|
|
|
(b0 | (b1 << 1) | (b2 << 2) | (b3 << 3) | (b4 << 4) | (b5 << 5) |
|
|
|
|
(b6 << 6) | (b7 << 7) | (b8 << 8));
|
|
|
|
outrow[col_idx] = 255 * (lut[lut_idx] & 1);
|
|
|
|
num_changed_pixels += ((b4 & 1) != (outrow[col_idx] & 1));
|
2014-06-05 00:03:00 +04:00
|
|
|
}
|
|
|
|
}
|
2021-01-03 06:17:51 +03:00
|
|
|
return Py_BuildValue("i", num_changed_pixels);
|
2014-06-05 00:03:00 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Match a morphologic LUT to a binary image and return a list
|
2018-03-10 06:48:01 +03:00
|
|
|
of the coordinates of all matching pixels.
|
2014-06-05 00:03:00 +04:00
|
|
|
|
|
|
|
Expected parameters:
|
|
|
|
|
|
|
|
1. a LUT - a 512 byte size lookup table.
|
|
|
|
2. an input Imaging image id.
|
|
|
|
|
|
|
|
Returns list of matching pixels.
|
|
|
|
*/
|
2021-01-03 06:17:51 +03:00
|
|
|
static PyObject *
|
|
|
|
match(PyObject *self, PyObject *args) {
|
2014-06-05 00:03:00 +04:00
|
|
|
const char *lut;
|
2014-06-20 01:54:39 +04:00
|
|
|
PyObject *py_lut;
|
2014-06-05 00:03:00 +04:00
|
|
|
Py_ssize_t lut_len, i0;
|
|
|
|
Imaging imgin;
|
|
|
|
int width, height;
|
|
|
|
int row_idx, col_idx;
|
|
|
|
UINT8 **inrows;
|
|
|
|
PyObject *ret = PyList_New(0);
|
|
|
|
|
2014-06-20 01:54:39 +04:00
|
|
|
if (!PyArg_ParseTuple(args, "On", &py_lut, &i0)) {
|
2014-06-05 00:03:00 +04:00
|
|
|
PyErr_SetString(PyExc_RuntimeError, "Argument parsing problem");
|
2014-06-20 01:54:39 +04:00
|
|
|
return NULL;
|
|
|
|
}
|
2014-06-05 00:03:00 +04:00
|
|
|
|
2014-06-24 03:02:29 +04:00
|
|
|
if (!PyBytes_Check(py_lut)) {
|
|
|
|
PyErr_SetString(PyExc_RuntimeError, "The morphology LUT is not a bytes object");
|
2014-06-05 00:03:00 +04:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2014-06-24 03:02:29 +04:00
|
|
|
lut_len = PyBytes_Size(py_lut);
|
2014-06-20 01:54:39 +04:00
|
|
|
|
2014-06-05 00:03:00 +04:00
|
|
|
if (lut_len < LUT_SIZE) {
|
|
|
|
PyErr_SetString(PyExc_RuntimeError, "The morphology LUT has the wrong size");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2014-06-24 03:02:29 +04:00
|
|
|
lut = PyBytes_AsString(py_lut);
|
2021-01-03 06:17:51 +03:00
|
|
|
imgin = (Imaging)i0;
|
2014-06-05 00:03:00 +04:00
|
|
|
|
2021-01-03 06:17:51 +03:00
|
|
|
if (imgin->type != IMAGING_TYPE_UINT8 || imgin->bands != 1) {
|
2018-04-22 20:52:38 +03:00
|
|
|
PyErr_SetString(PyExc_RuntimeError, "Unsupported image type");
|
2014-06-05 00:03:00 +04:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
inrows = imgin->image8;
|
|
|
|
width = imgin->xsize;
|
|
|
|
height = imgin->ysize;
|
|
|
|
|
2021-01-03 06:17:51 +03:00
|
|
|
for (row_idx = 1; row_idx < height - 1; row_idx++) {
|
2014-06-05 00:03:00 +04:00
|
|
|
UINT8 *inrow = inrows[row_idx];
|
|
|
|
UINT8 *prow, *nrow;
|
|
|
|
|
2021-01-03 06:17:51 +03:00
|
|
|
prow = inrows[row_idx - 1];
|
|
|
|
nrow = inrows[row_idx + 1];
|
|
|
|
|
|
|
|
for (col_idx = 1; col_idx < width - 1; col_idx++) {
|
|
|
|
int cim = col_idx - 1;
|
|
|
|
int cip = col_idx + 1;
|
|
|
|
unsigned char b0 = prow[cim] & 1;
|
|
|
|
unsigned char b1 = prow[col_idx] & 1;
|
|
|
|
unsigned char b2 = prow[cip] & 1;
|
|
|
|
|
|
|
|
unsigned char b3 = inrow[cim] & 1;
|
|
|
|
unsigned char b4 = inrow[col_idx] & 1;
|
|
|
|
unsigned char b5 = inrow[cip] & 1;
|
|
|
|
|
|
|
|
unsigned char b6 = nrow[cim] & 1;
|
|
|
|
unsigned char b7 = nrow[col_idx] & 1;
|
|
|
|
unsigned char b8 = nrow[cip] & 1;
|
|
|
|
|
|
|
|
int lut_idx =
|
|
|
|
(b0 | (b1 << 1) | (b2 << 2) | (b3 << 3) | (b4 << 4) | (b5 << 5) |
|
|
|
|
(b6 << 6) | (b7 << 7) | (b8 << 8));
|
2014-06-05 00:03:00 +04:00
|
|
|
if (lut[lut_idx]) {
|
2021-01-03 06:17:51 +03:00
|
|
|
PyObject *coordObj = Py_BuildValue("(nn)", col_idx, row_idx);
|
2014-06-05 00:03:00 +04:00
|
|
|
PyList_Append(ret, coordObj);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Return a list of the coordinates of all turned on pixels in an image.
|
2018-06-26 15:07:40 +03:00
|
|
|
May be used to extract features after a sequence of MorphOps were applied.
|
2014-06-05 00:03:00 +04:00
|
|
|
This is faster than match as only 1x1 lookup is made.
|
|
|
|
*/
|
2021-01-03 06:17:51 +03:00
|
|
|
static PyObject *
|
|
|
|
get_on_pixels(PyObject *self, PyObject *args) {
|
2014-06-05 00:03:00 +04:00
|
|
|
Py_ssize_t i0;
|
|
|
|
Imaging img;
|
|
|
|
UINT8 **rows;
|
|
|
|
int row_idx, col_idx;
|
|
|
|
int width, height;
|
|
|
|
PyObject *ret = PyList_New(0);
|
2016-09-03 05:23:42 +03:00
|
|
|
|
2014-06-05 00:03:00 +04:00
|
|
|
if (!PyArg_ParseTuple(args, "n", &i0)) {
|
|
|
|
PyErr_SetString(PyExc_RuntimeError, "Argument parsing problem");
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
2021-01-03 06:17:51 +03:00
|
|
|
img = (Imaging)i0;
|
2014-06-05 00:03:00 +04:00
|
|
|
rows = img->image8;
|
|
|
|
width = img->xsize;
|
|
|
|
height = img->ysize;
|
2016-09-03 05:23:42 +03:00
|
|
|
|
2021-01-03 06:17:51 +03:00
|
|
|
for (row_idx = 0; row_idx < height; row_idx++) {
|
2014-06-05 00:03:00 +04:00
|
|
|
UINT8 *row = rows[row_idx];
|
2021-01-03 06:17:51 +03:00
|
|
|
for (col_idx = 0; col_idx < width; col_idx++) {
|
2014-06-05 00:03:00 +04:00
|
|
|
if (row[col_idx]) {
|
2021-01-03 06:17:51 +03:00
|
|
|
PyObject *coordObj = Py_BuildValue("(nn)", col_idx, row_idx);
|
2014-06-05 00:03:00 +04:00
|
|
|
PyList_Append(ret, coordObj);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2021-01-03 06:17:51 +03:00
|
|
|
setup_module(PyObject *m) {
|
|
|
|
PyObject *d = PyModule_GetDict(m);
|
2014-06-05 00:03:00 +04:00
|
|
|
|
|
|
|
PyDict_SetItemString(d, "__version", PyUnicode_FromString("0.1"));
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static PyMethodDef functions[] = {
|
|
|
|
/* Functions */
|
2014-06-25 06:06:05 +04:00
|
|
|
{"apply", (PyCFunction)apply, METH_VARARGS, NULL},
|
|
|
|
{"get_on_pixels", (PyCFunction)get_on_pixels, METH_VARARGS, NULL},
|
|
|
|
{"match", (PyCFunction)match, METH_VARARGS, NULL},
|
2021-01-03 06:17:51 +03:00
|
|
|
{NULL, NULL, 0, NULL}};
|
2014-06-05 00:03:00 +04:00
|
|
|
|
|
|
|
PyMODINIT_FUNC
|
|
|
|
PyInit__imagingmorph(void) {
|
2021-01-03 06:17:51 +03:00
|
|
|
PyObject *m;
|
2014-06-05 00:03:00 +04:00
|
|
|
|
|
|
|
static PyModuleDef module_def = {
|
|
|
|
PyModuleDef_HEAD_INIT,
|
2021-01-03 06:17:51 +03:00
|
|
|
"_imagingmorph", /* m_name */
|
|
|
|
"A module for doing image morphology", /* m_doc */
|
|
|
|
-1, /* m_size */
|
|
|
|
functions, /* m_methods */
|
2014-06-05 00:03:00 +04:00
|
|
|
};
|
|
|
|
|
|
|
|
m = PyModule_Create(&module_def);
|
|
|
|
|
2020-05-10 12:56:36 +03:00
|
|
|
if (setup_module(m) < 0) {
|
2014-06-05 00:03:00 +04:00
|
|
|
return NULL;
|
2020-05-10 12:56:36 +03:00
|
|
|
}
|
2014-06-05 00:03:00 +04:00
|
|
|
|
|
|
|
return m;
|
|
|
|
}
|