| File: | _imagingmorph.c |
| Warning: | line 231, column 38 PyObject ownership leak with reference count of 1 |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
| 1 | /* | |||
| 2 | * The Python Imaging Library | |||
| 3 | * | |||
| 4 | * A binary morphology add-on for the Python Imaging Library | |||
| 5 | * | |||
| 6 | * History: | |||
| 7 | * 2014-06-04 Initial version. | |||
| 8 | * | |||
| 9 | * Copyright (c) 2014 Dov Grobgeld <dov.grobgeld@gmail.com> | |||
| 10 | * | |||
| 11 | * See the README file for information on usage and redistribution. | |||
| 12 | */ | |||
| 13 | ||||
| 14 | #include "Python.h" | |||
| 15 | #include "libImaging/Imaging.h" | |||
| 16 | ||||
| 17 | #define LUT_SIZE(1 << 9) (1 << 9) | |||
| 18 | ||||
| 19 | /* Apply a morphologic LUT to a binary image. Outputs a | |||
| 20 | a new binary image. | |||
| 21 | ||||
| 22 | Expected parameters: | |||
| 23 | ||||
| 24 | 1. a LUT - a 512 byte size lookup table. | |||
| 25 | 2. an input Imaging image id. | |||
| 26 | 3. an output Imaging image id | |||
| 27 | ||||
| 28 | Returns number of changed pixels. | |||
| 29 | */ | |||
| 30 | static PyObject * | |||
| 31 | apply(PyObject *self, PyObject *args) { | |||
| 32 | const char *lut; | |||
| 33 | PyObject *py_lut; | |||
| 34 | Py_ssize_t lut_len, i0, i1; | |||
| 35 | Imaging imgin, imgout; | |||
| 36 | int width, height; | |||
| 37 | int row_idx, col_idx; | |||
| 38 | UINT8unsigned char **inrows, **outrows; | |||
| 39 | int num_changed_pixels = 0; | |||
| 40 | ||||
| 41 | if (!PyArg_ParseTuple(args, "Onn", &py_lut, &i0, &i1)) { | |||
| 42 | PyErr_SetString(PyExc_RuntimeError, "Argument parsing problem"); | |||
| 43 | return NULL((void*)0); | |||
| 44 | } | |||
| 45 | ||||
| 46 | if (!PyBytes_Check(py_lut)((((((PyObject*)(py_lut))->ob_type))->tp_flags & (( 1UL << 27))) != 0)) { | |||
| 47 | PyErr_SetString(PyExc_RuntimeError, "The morphology LUT is not a bytes object"); | |||
| 48 | return NULL((void*)0); | |||
| 49 | } | |||
| 50 | ||||
| 51 | lut_len = PyBytes_Size(py_lut); | |||
| 52 | ||||
| 53 | if (lut_len < LUT_SIZE(1 << 9)) { | |||
| 54 | PyErr_SetString(PyExc_RuntimeError, "The morphology LUT has the wrong size"); | |||
| 55 | return NULL((void*)0); | |||
| 56 | } | |||
| 57 | ||||
| 58 | lut = PyBytes_AsString(py_lut); | |||
| 59 | ||||
| 60 | imgin = (Imaging)i0; | |||
| 61 | imgout = (Imaging)i1; | |||
| 62 | width = imgin->xsize; | |||
| 63 | height = imgin->ysize; | |||
| 64 | ||||
| 65 | if (imgin->type != IMAGING_TYPE_UINT80 || imgin->bands != 1) { | |||
| 66 | PyErr_SetString(PyExc_RuntimeError, "Unsupported image type"); | |||
| 67 | return NULL((void*)0); | |||
| 68 | } | |||
| 69 | if (imgout->type != IMAGING_TYPE_UINT80 || imgout->bands != 1) { | |||
| 70 | PyErr_SetString(PyExc_RuntimeError, "Unsupported image type"); | |||
| 71 | return NULL((void*)0); | |||
| 72 | } | |||
| 73 | ||||
| 74 | inrows = imgin->image8; | |||
| 75 | outrows = imgout->image8; | |||
| 76 | ||||
| 77 | for (row_idx = 0; row_idx < height; row_idx++) { | |||
| 78 | UINT8unsigned char *outrow = outrows[row_idx]; | |||
| 79 | UINT8unsigned char *inrow = inrows[row_idx]; | |||
| 80 | UINT8unsigned char *prow, *nrow; /* Previous and next row */ | |||
| 81 | ||||
| 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++) { | |||
| 86 | outrow[col_idx] = 0; | |||
| 87 | } | |||
| 88 | continue; | |||
| 89 | } | |||
| 90 | ||||
| 91 | prow = inrows[row_idx - 1]; | |||
| 92 | nrow = inrows[row_idx + 1]; | |||
| 93 | ||||
| 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; | |||
| 100 | ||||
| 101 | unsigned char b3 = inrow[cim] & 1; | |||
| 102 | unsigned char b4 = inrow[col_idx] & 1; | |||
| 103 | unsigned char b5 = inrow[cip] & 1; | |||
| 104 | ||||
| 105 | unsigned char b6 = nrow[cim] & 1; | |||
| 106 | unsigned char b7 = nrow[col_idx] & 1; | |||
| 107 | unsigned char b8 = nrow[cip] & 1; | |||
| 108 | ||||
| 109 | int lut_idx = | |||
| 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)); | |||
| 114 | } | |||
| 115 | } | |||
| 116 | return Py_BuildValue("i", num_changed_pixels); | |||
| 117 | } | |||
| 118 | ||||
| 119 | /* Match a morphologic LUT to a binary image and return a list | |||
| 120 | of the coordinates of all matching pixels. | |||
| 121 | ||||
| 122 | Expected parameters: | |||
| 123 | ||||
| 124 | 1. a LUT - a 512 byte size lookup table. | |||
| 125 | 2. an input Imaging image id. | |||
| 126 | ||||
| 127 | Returns list of matching pixels. | |||
| 128 | */ | |||
| 129 | static PyObject * | |||
| 130 | match(PyObject *self, PyObject *args) { | |||
| 131 | const char *lut; | |||
| 132 | PyObject *py_lut; | |||
| 133 | Py_ssize_t lut_len, i0; | |||
| 134 | Imaging imgin; | |||
| 135 | int width, height; | |||
| 136 | int row_idx, col_idx; | |||
| 137 | UINT8unsigned char **inrows; | |||
| 138 | PyObject *ret = PyList_New(0); | |||
| 139 | ||||
| 140 | if (!PyArg_ParseTuple(args, "On", &py_lut, &i0)) { | |||
| 141 | PyErr_SetString(PyExc_RuntimeError, "Argument parsing problem"); | |||
| 142 | return NULL((void*)0); | |||
| 143 | } | |||
| 144 | ||||
| 145 | if (!PyBytes_Check(py_lut)((((((PyObject*)(py_lut))->ob_type))->tp_flags & (( 1UL << 27))) != 0)) { | |||
| 146 | PyErr_SetString(PyExc_RuntimeError, "The morphology LUT is not a bytes object"); | |||
| 147 | return NULL((void*)0); | |||
| 148 | } | |||
| 149 | ||||
| 150 | lut_len = PyBytes_Size(py_lut); | |||
| 151 | ||||
| 152 | if (lut_len < LUT_SIZE(1 << 9)) { | |||
| 153 | PyErr_SetString(PyExc_RuntimeError, "The morphology LUT has the wrong size"); | |||
| 154 | return NULL((void*)0); | |||
| 155 | } | |||
| 156 | ||||
| 157 | lut = PyBytes_AsString(py_lut); | |||
| 158 | imgin = (Imaging)i0; | |||
| 159 | ||||
| 160 | if (imgin->type != IMAGING_TYPE_UINT80 || imgin->bands != 1) { | |||
| 161 | PyErr_SetString(PyExc_RuntimeError, "Unsupported image type"); | |||
| 162 | return NULL((void*)0); | |||
| 163 | } | |||
| 164 | ||||
| 165 | inrows = imgin->image8; | |||
| 166 | width = imgin->xsize; | |||
| 167 | height = imgin->ysize; | |||
| 168 | ||||
| 169 | for (row_idx = 1; row_idx < height - 1; row_idx++) { | |||
| 170 | UINT8unsigned char *inrow = inrows[row_idx]; | |||
| 171 | UINT8unsigned char *prow, *nrow; | |||
| 172 | ||||
| 173 | prow = inrows[row_idx - 1]; | |||
| 174 | nrow = inrows[row_idx + 1]; | |||
| 175 | ||||
| 176 | for (col_idx = 1; col_idx < width - 1; col_idx++) { | |||
| 177 | int cim = col_idx - 1; | |||
| 178 | int cip = col_idx + 1; | |||
| 179 | unsigned char b0 = prow[cim] & 1; | |||
| 180 | unsigned char b1 = prow[col_idx] & 1; | |||
| 181 | unsigned char b2 = prow[cip] & 1; | |||
| 182 | ||||
| 183 | unsigned char b3 = inrow[cim] & 1; | |||
| 184 | unsigned char b4 = inrow[col_idx] & 1; | |||
| 185 | unsigned char b5 = inrow[cip] & 1; | |||
| 186 | ||||
| 187 | unsigned char b6 = nrow[cim] & 1; | |||
| 188 | unsigned char b7 = nrow[col_idx] & 1; | |||
| 189 | unsigned char b8 = nrow[cip] & 1; | |||
| 190 | ||||
| 191 | int lut_idx = | |||
| 192 | (b0 | (b1 << 1) | (b2 << 2) | (b3 << 3) | (b4 << 4) | (b5 << 5) | | |||
| 193 | (b6 << 6) | (b7 << 7) | (b8 << 8)); | |||
| 194 | if (lut[lut_idx]) { | |||
| 195 | PyObject *coordObj = Py_BuildValue("(nn)", col_idx, row_idx); | |||
| 196 | PyList_Append(ret, coordObj); | |||
| 197 | } | |||
| 198 | } | |||
| 199 | } | |||
| 200 | ||||
| 201 | return ret; | |||
| 202 | } | |||
| 203 | ||||
| 204 | /* Return a list of the coordinates of all turned on pixels in an image. | |||
| 205 | May be used to extract features after a sequence of MorphOps were applied. | |||
| 206 | This is faster than match as only 1x1 lookup is made. | |||
| 207 | */ | |||
| 208 | static PyObject * | |||
| 209 | get_on_pixels(PyObject *self, PyObject *args) { | |||
| 210 | Py_ssize_t i0; | |||
| 211 | Imaging img; | |||
| 212 | UINT8unsigned char **rows; | |||
| 213 | int row_idx, col_idx; | |||
| 214 | int width, height; | |||
| 215 | PyObject *ret = PyList_New(0); | |||
| 216 | ||||
| 217 | if (!PyArg_ParseTuple(args, "n", &i0)) { | |||
| ||||
| 218 | PyErr_SetString(PyExc_RuntimeError, "Argument parsing problem"); | |||
| 219 | ||||
| 220 | return NULL((void*)0); | |||
| 221 | } | |||
| 222 | img = (Imaging)i0; | |||
| 223 | rows = img->image8; | |||
| 224 | width = img->xsize; | |||
| 225 | height = img->ysize; | |||
| 226 | ||||
| 227 | for (row_idx = 0; row_idx < height; row_idx++) { | |||
| 228 | UINT8unsigned char *row = rows[row_idx]; | |||
| 229 | for (col_idx = 0; col_idx < width; col_idx++) { | |||
| 230 | if (row[col_idx]) { | |||
| 231 | PyObject *coordObj = Py_BuildValue("(nn)", col_idx, row_idx); | |||
| ||||
| 232 | PyList_Append(ret, coordObj); | |||
| 233 | } | |||
| 234 | } | |||
| 235 | } | |||
| 236 | return ret; | |||
| 237 | } | |||
| 238 | ||||
| 239 | static int | |||
| 240 | setup_module(PyObject *m) { | |||
| 241 | PyObject *d = PyModule_GetDict(m); | |||
| 242 | ||||
| 243 | PyDict_SetItemString(d, "__version", PyUnicode_FromString("0.1")); | |||
| 244 | ||||
| 245 | return 0; | |||
| 246 | } | |||
| 247 | ||||
| 248 | static PyMethodDef functions[] = { | |||
| 249 | /* Functions */ | |||
| 250 | {"apply", (PyCFunction)apply, METH_VARARGS0x0001, NULL((void*)0)}, | |||
| 251 | {"get_on_pixels", (PyCFunction)get_on_pixels, METH_VARARGS0x0001, NULL((void*)0)}, | |||
| 252 | {"match", (PyCFunction)match, METH_VARARGS0x0001, NULL((void*)0)}, | |||
| 253 | {NULL((void*)0), NULL((void*)0), 0, NULL((void*)0)}}; | |||
| 254 | ||||
| 255 | PyMODINIT_FUNCPyObject* | |||
| 256 | PyInit__imagingmorph(void) { | |||
| 257 | PyObject *m; | |||
| 258 | ||||
| 259 | static PyModuleDef module_def = { | |||
| 260 | PyModuleDef_HEAD_INIT{ { 1, ((void*)0) }, ((void*)0), 0, ((void*)0), }, | |||
| 261 | "_imagingmorph", /* m_name */ | |||
| 262 | "A module for doing image morphology", /* m_doc */ | |||
| 263 | -1, /* m_size */ | |||
| 264 | functions, /* m_methods */ | |||
| 265 | }; | |||
| 266 | ||||
| 267 | m = PyModule_Create(&module_def)PyModule_Create2(&module_def, 1013); | |||
| 268 | ||||
| 269 | if (setup_module(m) < 0) { | |||
| 270 | return NULL((void*)0); | |||
| 271 | } | |||
| 272 | ||||
| 273 | return m; | |||
| 274 | } |
| 1 | #ifndef Py_BuildValue |
| 2 | struct _object; |
| 3 | typedef struct _object PyObject; |
| 4 | PyObject* clang_analyzer_PyObject_New_Reference(); |
| 5 | PyObject* Py_BuildValue(const char *format, ...) { |
| 6 | return clang_analyzer_PyObject_New_Reference(); |
| 7 | } |
| 8 | #else |
| 9 | #warning "API Py_BuildValue is defined as a macro." |
| 10 | #endif |