123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273 |
- /*
- * 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"
- #include "libImaging/Imaging.h"
- #define LUT_SIZE (1 << 9)
- /* 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.
- */
- static PyObject *
- apply(PyObject *self, PyObject *args) {
- const char *lut;
- PyObject *py_lut;
- 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;
- if (!PyArg_ParseTuple(args, "Onn", &py_lut, &i0, &i1)) {
- PyErr_SetString(PyExc_RuntimeError, "Argument parsing problem");
- return NULL;
- }
- if (!PyBytes_Check(py_lut)) {
- PyErr_SetString(PyExc_RuntimeError, "The morphology LUT is not a bytes object");
- return NULL;
- }
- lut_len = PyBytes_Size(py_lut);
- if (lut_len < LUT_SIZE) {
- PyErr_SetString(PyExc_RuntimeError, "The morphology LUT has the wrong size");
- return NULL;
- }
- lut = PyBytes_AsString(py_lut);
- imgin = (Imaging)i0;
- imgout = (Imaging)i1;
- width = imgin->xsize;
- height = imgin->ysize;
- if (imgin->type != IMAGING_TYPE_UINT8 || imgin->bands != 1) {
- PyErr_SetString(PyExc_RuntimeError, "Unsupported image type");
- return NULL;
- }
- if (imgout->type != IMAGING_TYPE_UINT8 || imgout->bands != 1) {
- PyErr_SetString(PyExc_RuntimeError, "Unsupported image type");
- return NULL;
- }
- inrows = imgin->image8;
- outrows = imgout->image8;
- for (row_idx = 0; row_idx < height; row_idx++) {
- UINT8 *outrow = outrows[row_idx];
- UINT8 *inrow = inrows[row_idx];
- UINT8 *prow, *nrow; /* Previous and next row */
- /* zero boundary conditions. TBD support other modes */
- outrow[0] = outrow[width - 1] = 0;
- if (row_idx == 0 || row_idx == height - 1) {
- for (col_idx = 0; col_idx < width; col_idx++) {
- outrow[col_idx] = 0;
- }
- continue;
- }
- 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));
- }
- }
- return Py_BuildValue("i", num_changed_pixels);
- }
- /* Match a morphologic LUT to a binary image and return a list
- of the coordinates of all matching pixels.
- Expected parameters:
- 1. a LUT - a 512 byte size lookup table.
- 2. an input Imaging image id.
- Returns list of matching pixels.
- */
- static PyObject *
- match(PyObject *self, PyObject *args) {
- const char *lut;
- PyObject *py_lut;
- Py_ssize_t lut_len, i0;
- Imaging imgin;
- int width, height;
- int row_idx, col_idx;
- UINT8 **inrows;
- PyObject *ret = PyList_New(0);
- if (ret == NULL) {
- return NULL;
- }
- if (!PyArg_ParseTuple(args, "On", &py_lut, &i0)) {
- Py_DECREF(ret);
- PyErr_SetString(PyExc_RuntimeError, "Argument parsing problem");
- return NULL;
- }
- if (!PyBytes_Check(py_lut)) {
- Py_DECREF(ret);
- PyErr_SetString(PyExc_RuntimeError, "The morphology LUT is not a bytes object");
- return NULL;
- }
- lut_len = PyBytes_Size(py_lut);
- if (lut_len < LUT_SIZE) {
- Py_DECREF(ret);
- PyErr_SetString(PyExc_RuntimeError, "The morphology LUT has the wrong size");
- return NULL;
- }
- lut = PyBytes_AsString(py_lut);
- imgin = (Imaging)i0;
- if (imgin->type != IMAGING_TYPE_UINT8 || imgin->bands != 1) {
- Py_DECREF(ret);
- PyErr_SetString(PyExc_RuntimeError, "Unsupported image type");
- return NULL;
- }
- inrows = imgin->image8;
- width = imgin->xsize;
- height = imgin->ysize;
- for (row_idx = 1; row_idx < height - 1; row_idx++) {
- UINT8 *inrow = inrows[row_idx];
- UINT8 *prow, *nrow;
- 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));
- if (lut[lut_idx]) {
- PyObject *coordObj = Py_BuildValue("(nn)", col_idx, row_idx);
- PyList_Append(ret, coordObj);
- Py_XDECREF(coordObj);
- }
- }
- }
- return ret;
- }
- /* Return a list of the coordinates of all turned on pixels in an image.
- May be used to extract features after a sequence of MorphOps were applied.
- This is faster than match as only 1x1 lookup is made.
- */
- static PyObject *
- get_on_pixels(PyObject *self, PyObject *args) {
- Py_ssize_t i0;
- Imaging img;
- UINT8 **rows;
- int row_idx, col_idx;
- int width, height;
- PyObject *ret = PyList_New(0);
- if (ret == NULL) {
- return NULL;
- }
- if (!PyArg_ParseTuple(args, "n", &i0)) {
- Py_DECREF(ret);
- PyErr_SetString(PyExc_RuntimeError, "Argument parsing problem");
- return NULL;
- }
- img = (Imaging)i0;
- rows = img->image8;
- width = img->xsize;
- height = img->ysize;
- for (row_idx = 0; row_idx < height; row_idx++) {
- UINT8 *row = rows[row_idx];
- for (col_idx = 0; col_idx < width; col_idx++) {
- if (row[col_idx]) {
- PyObject *coordObj = Py_BuildValue("(nn)", col_idx, row_idx);
- PyList_Append(ret, coordObj);
- Py_XDECREF(coordObj);
- }
- }
- }
- return ret;
- }
- static PyMethodDef functions[] = {
- /* Functions */
- {"apply", (PyCFunction)apply, METH_VARARGS, NULL},
- {"get_on_pixels", (PyCFunction)get_on_pixels, METH_VARARGS, NULL},
- {"match", (PyCFunction)match, METH_VARARGS, NULL},
- {NULL, NULL, 0, NULL}};
- PyMODINIT_FUNC
- PyInit__imagingmorph(void) {
- PyObject *m;
- static PyModuleDef module_def = {
- PyModuleDef_HEAD_INIT,
- "_imagingmorph", /* m_name */
- "A module for doing image morphology", /* m_doc */
- -1, /* m_size */
- functions, /* m_methods */
- };
- m = PyModule_Create(&module_def);
- return m;
- }
|