_imagingmorph.c 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304
  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. #include "Python.h"
  14. #include "Imaging.h"
  15. #include "py3.h"
  16. #define LUT_SIZE (1<<9)
  17. /* Apply a morphologic LUT to a binary image. Outputs a
  18. a new binary image.
  19. Expected parameters:
  20. 1. a LUT - a 512 byte size lookup table.
  21. 2. an input Imaging image id.
  22. 3. an output Imaging image id
  23. Returns number of changed pixels.
  24. */
  25. static PyObject*
  26. apply(PyObject *self, PyObject* args)
  27. {
  28. const char *lut;
  29. PyObject *py_lut;
  30. Py_ssize_t lut_len, i0, i1;
  31. Imaging imgin, imgout;
  32. int width, height;
  33. int row_idx, col_idx;
  34. UINT8 **inrows, **outrows;
  35. int num_changed_pixels = 0;
  36. if (!PyArg_ParseTuple(args, "Onn", &py_lut, &i0, &i1)) {
  37. PyErr_SetString(PyExc_RuntimeError, "Argument parsing problem");
  38. return NULL;
  39. }
  40. if (!PyBytes_Check(py_lut)) {
  41. PyErr_SetString(PyExc_RuntimeError, "The morphology LUT is not a bytes object");
  42. return NULL;
  43. }
  44. lut_len = PyBytes_Size(py_lut);
  45. if (lut_len < LUT_SIZE) {
  46. PyErr_SetString(PyExc_RuntimeError, "The morphology LUT has the wrong size");
  47. return NULL;
  48. }
  49. lut = PyBytes_AsString(py_lut);
  50. imgin = (Imaging) i0;
  51. imgout = (Imaging) i1;
  52. width = imgin->xsize;
  53. height = imgin->ysize;
  54. if (imgin->type != IMAGING_TYPE_UINT8 ||
  55. imgin->bands != 1) {
  56. PyErr_SetString(PyExc_RuntimeError, "Unsupported image type");
  57. return NULL;
  58. }
  59. if (imgout->type != IMAGING_TYPE_UINT8 ||
  60. imgout->bands != 1) {
  61. PyErr_SetString(PyExc_RuntimeError, "Unsupported image type");
  62. return NULL;
  63. }
  64. inrows = imgin->image8;
  65. outrows = imgout->image8;
  66. for (row_idx=0; row_idx < height; row_idx++) {
  67. UINT8 *outrow = outrows[row_idx];
  68. UINT8 *inrow = inrows[row_idx];
  69. UINT8 *prow, *nrow; /* Previous and next row */
  70. /* zero boundary conditions. TBD support other modes */
  71. outrow[0] = outrow[width-1] = 0;
  72. if (row_idx==0 || row_idx == height-1) {
  73. for(col_idx=0; col_idx<width; col_idx++)
  74. outrow[col_idx] = 0;
  75. continue;
  76. }
  77. prow = inrows[row_idx-1];
  78. nrow = inrows[row_idx+1];
  79. for (col_idx=1; col_idx<width-1; col_idx++) {
  80. int cim = col_idx-1;
  81. int cip = col_idx+1;
  82. unsigned char b0 = prow[cim] &1;
  83. unsigned char b1 = prow[col_idx]&1;
  84. unsigned char b2 = prow[cip]&1;
  85. unsigned char b3 = inrow[cim]&1;
  86. unsigned char b4 = inrow[col_idx]&1;
  87. unsigned char b5 = inrow[cip]&1;
  88. unsigned char b6 = nrow[cim]&1;
  89. unsigned char b7 = nrow[col_idx]&1;
  90. unsigned char b8 = nrow[cip]&1;
  91. int lut_idx = (b0
  92. |(b1 << 1)
  93. |(b2 << 2)
  94. |(b3 << 3)
  95. |(b4 << 4)
  96. |(b5 << 5)
  97. |(b6 << 6)
  98. |(b7 << 7)
  99. |(b8 << 8));
  100. outrow[col_idx] = 255*(lut[lut_idx]&1);
  101. num_changed_pixels += ((b4&1)!=(outrow[col_idx]&1));
  102. }
  103. }
  104. return Py_BuildValue("i",num_changed_pixels);
  105. }
  106. /* Match a morphologic LUT to a binary image and return a list
  107. of the coordinates of all matching pixels.
  108. Expected parameters:
  109. 1. a LUT - a 512 byte size lookup table.
  110. 2. an input Imaging image id.
  111. Returns list of matching pixels.
  112. */
  113. static PyObject*
  114. match(PyObject *self, PyObject* args)
  115. {
  116. const char *lut;
  117. PyObject *py_lut;
  118. Py_ssize_t lut_len, i0;
  119. Imaging imgin;
  120. int width, height;
  121. int row_idx, col_idx;
  122. UINT8 **inrows;
  123. PyObject *ret = PyList_New(0);
  124. if (!PyArg_ParseTuple(args, "On", &py_lut, &i0)) {
  125. PyErr_SetString(PyExc_RuntimeError, "Argument parsing problem");
  126. return NULL;
  127. }
  128. if (!PyBytes_Check(py_lut)) {
  129. PyErr_SetString(PyExc_RuntimeError, "The morphology LUT is not a bytes object");
  130. return NULL;
  131. }
  132. lut_len = PyBytes_Size(py_lut);
  133. if (lut_len < LUT_SIZE) {
  134. PyErr_SetString(PyExc_RuntimeError, "The morphology LUT has the wrong size");
  135. return NULL;
  136. }
  137. lut = PyBytes_AsString(py_lut);
  138. imgin = (Imaging) i0;
  139. if (imgin->type != IMAGING_TYPE_UINT8 ||
  140. imgin->bands != 1) {
  141. PyErr_SetString(PyExc_RuntimeError, "Unsupported image type");
  142. return NULL;
  143. }
  144. inrows = imgin->image8;
  145. width = imgin->xsize;
  146. height = imgin->ysize;
  147. for (row_idx=1; row_idx < height-1; row_idx++) {
  148. UINT8 *inrow = inrows[row_idx];
  149. UINT8 *prow, *nrow;
  150. prow = inrows[row_idx-1];
  151. nrow = inrows[row_idx+1];
  152. for (col_idx=1; col_idx<width-1; col_idx++) {
  153. int cim = col_idx-1;
  154. int cip = col_idx+1;
  155. unsigned char b0 = prow[cim] &1;
  156. unsigned char b1 = prow[col_idx]&1;
  157. unsigned char b2 = prow[cip]&1;
  158. unsigned char b3 = inrow[cim]&1;
  159. unsigned char b4 = inrow[col_idx]&1;
  160. unsigned char b5 = inrow[cip]&1;
  161. unsigned char b6 = nrow[cim]&1;
  162. unsigned char b7 = nrow[col_idx]&1;
  163. unsigned char b8 = nrow[cip]&1;
  164. int lut_idx = (b0
  165. |(b1 << 1)
  166. |(b2 << 2)
  167. |(b3 << 3)
  168. |(b4 << 4)
  169. |(b5 << 5)
  170. |(b6 << 6)
  171. |(b7 << 7)
  172. |(b8 << 8));
  173. if (lut[lut_idx]) {
  174. PyObject *coordObj = Py_BuildValue("(nn)",col_idx,row_idx);
  175. PyList_Append(ret, coordObj);
  176. }
  177. }
  178. }
  179. return ret;
  180. }
  181. /* Return a list of the coordinates of all turned on pixels in an image.
  182. May be used to extract features after a sequence of MorphOps were applied.
  183. This is faster than match as only 1x1 lookup is made.
  184. */
  185. static PyObject*
  186. get_on_pixels(PyObject *self, PyObject* args)
  187. {
  188. Py_ssize_t i0;
  189. Imaging img;
  190. UINT8 **rows;
  191. int row_idx, col_idx;
  192. int width, height;
  193. PyObject *ret = PyList_New(0);
  194. if (!PyArg_ParseTuple(args, "n", &i0)) {
  195. PyErr_SetString(PyExc_RuntimeError, "Argument parsing problem");
  196. return NULL;
  197. }
  198. img = (Imaging) i0;
  199. rows = img->image8;
  200. width = img->xsize;
  201. height = img->ysize;
  202. for (row_idx=0; row_idx < height; row_idx++) {
  203. UINT8 *row = rows[row_idx];
  204. for (col_idx=0; col_idx<width; col_idx++) {
  205. if (row[col_idx]) {
  206. PyObject *coordObj = Py_BuildValue("(nn)",col_idx,row_idx);
  207. PyList_Append(ret, coordObj);
  208. }
  209. }
  210. }
  211. return ret;
  212. }
  213. static int
  214. setup_module(PyObject* m)
  215. {
  216. PyObject* d = PyModule_GetDict(m);
  217. PyDict_SetItemString(d, "__version", PyUnicode_FromString("0.1"));
  218. return 0;
  219. }
  220. static PyMethodDef functions[] = {
  221. /* Functions */
  222. {"apply", (PyCFunction)apply, METH_VARARGS, NULL},
  223. {"get_on_pixels", (PyCFunction)get_on_pixels, METH_VARARGS, NULL},
  224. {"match", (PyCFunction)match, METH_VARARGS, NULL},
  225. {NULL, NULL, 0, NULL}
  226. };
  227. #if PY_VERSION_HEX >= 0x03000000
  228. PyMODINIT_FUNC
  229. PyInit__imagingmorph(void) {
  230. PyObject* m;
  231. static PyModuleDef module_def = {
  232. PyModuleDef_HEAD_INIT,
  233. "_imagingmorph", /* m_name */
  234. "A module for doing image morphology", /* m_doc */
  235. -1, /* m_size */
  236. functions, /* m_methods */
  237. };
  238. m = PyModule_Create(&module_def);
  239. if (setup_module(m) < 0)
  240. return NULL;
  241. return m;
  242. }
  243. #else
  244. PyMODINIT_FUNC
  245. init_imagingmorph(void)
  246. {
  247. PyObject* m = Py_InitModule("_imagingmorph", functions);
  248. setup_module(m);
  249. }
  250. #endif