3

I have the following code in which I am trying to get a portion of a image corresponding to a mask I am given. Then I would like to apply the skimage.feature.glcm on that portion. But I get the error:

glcm = greycomatrix(mancha, [2], [0], levels=None, symmetric = True, normed = True)
File "D:\WinPython-64bit-2.7.13.1ZeroNew\python-2.7.13.amd64\lib\site-packages\skimage\feature\texture.py", line 101, in greycomatrix
assert_nD(image, 2)
File "D:\WinPython-64bit-2.7.13.1ZeroNew\python-2.7.13.amd64\lib\site-packages\skimage\_shared\utils.py", line 178, in assert_nD
raise ValueError(msg_incorrect_dim % (arg_name, '-or-'.join([str(n) for n in ndim])))
ValueError: The parameter `image` must be a 2-dimensional array

The code is:

mask = cv2.imread(pathMask, 0)
cruda = cv2.imread(pathCruda, 0)
imaskc = mask > 0
mancha = cruda[imaskc]

glcm = greycomatrix(mancha, [2], [0], levels=None, symmetric = True, normed = True)
energy = greycoprops(glcm, 'energy')
homogeneity = greycoprops(glcm, 'homogeneity')

I have also tried unsuccesfully with:

labeled_image, nb_labels = ndimage.label(mascara)
blobs = ndimage.find_objects(labeled_image)

glcm = greycomatrix(cruda[blobs[0]]

Any ideas how to get this done?

Thanks!

Tonechas
  • 13,398
  • 16
  • 46
  • 80
  • Indexing a numpy array with a mask returns a linear array. This is not that surprising though if you think about it. If `a = [[1, 2], [3, 4]]`, and you have a mask `b = [[1, 0], [1, 1]]`, the result `a[b] = [1, 3, 4]`. So it's not a 2-dimensional array. It can't return a 2D array if there are a different number of masked elements on each row, so it never does. However, if you just want to set everything outside the mask to black you can easily do that with `cruda[~imaskc] = 0`. – alkasm Dec 01 '17 at 11:08
  • Thanks for the answer, but what I would like to do is getting the portion of the image so that I can pass it to the glcm function – Adrián Arroyo Perez Dec 01 '17 at 11:31
  • Think about what you actually want here. Look at my example above where 1s are `True` and 0s are `False`. What *should* the return of `a[b]` look like in that example? – alkasm Dec 01 '17 at 21:21
  • Sorry, I dont really understando what you mean. You just said the return of a[b] = [1,2,3] – Adrián Arroyo Perez Dec 04 '17 at 10:01
  • Exactly...and how many dimensions is that array? Just one. `glcm` requires a two-dimensional input. Indexing with a mask just gives all the elements corresponding to the mask, linearly. In other words: if `a = np.array([[1, 2], [3, 4]])` and `b = np.array([[1, 0], [1, 1]])` then `a[b] = np.array([1, 3, 4])` but what you probably want is `a[~b] = 0`, that way `a = np.array([[1, 0], [3, 4]])`, which is two-dimensional. – alkasm Dec 04 '17 at 10:11
  • If you just want to get a region of interest specified by the mask, then find the min and max row and column indices of the white parts of the mask, and index your array `cruda[min_row:max_row, min_col:max_col]`. But you'll still need to mask beforehand if you want the values to be removed. – alkasm Dec 04 '17 at 10:17
  • So, If I just do cruda[~mask]=0 and then greycomatrix(cruda,...) it will work, is that what you mean? Or the output is not going to be as desired as the cruda image is black everywhere except the region inside mask? Or should I pass cruda[mask==255] after applying cruda[~mask]=0?? – Adrián Arroyo Perez Dec 04 '17 at 11:38
  • `cruda[~mask]=0` will set everywhere except the masked region to black---that's correct. Again, you *cannot pass* `cruda[mask==255]` into the function, as this masked indexing *always returns a linear array* and never returns a 2D array. I don't know what a `glcm` even is, so I don't know what the correct output is. I'm just stating different ways to mask or index the image to get what you want. – alkasm Dec 04 '17 at 11:45
  • what about the second part of the code? blobs[0] is supposed to be a slice, will it work as index returning 2D elements? – Adrián Arroyo Perez Dec 04 '17 at 12:13
  • What that does is gives the min and max locations of the mask as I mentioned previously. So yes, that would work. It creates a region of interest around the mask values. Note that you could also do `np.where()` and find the min/max x, y values resulting in the same fashion. But do note, again---this will only give you a subset of your image as a rectangle, not in the shape of the mask as it is impossible to do that. – alkasm Dec 04 '17 at 12:53
  • Take a look at [this answer](https://stackoverflow.com/questions/40703086/python-taking-the-glcm-of-a-non-rectangular-region/42837786#42837786) for a [mahotas](http://mahotas.readthedocs.io/en/latest/)-based workaround to your problem – Tonechas Dec 04 '17 at 13:35

1 Answers1

3

You can't directly pass a masked image to greycomatrix. The good news is that you can compute the approximate value of Haralick features extracted from a region of interest of an image by introducing small changes in your code.

The basic idea consists in saving a grey level, say 0, to flag those image pixels that fall outside the region of interest (ROI). For this approach to work properly, you need to change the intensity of those pixels inside the ROI whose original intensity was 0 to a different (but similar) value, for example 1. Notice that modifying the image in this way inevitably introduces inaccuracies in the co-occurrence matrix, but as long as your image is sufficiently large and has a smooth histogram, you can safely assume that the obtained features are a pretty good approximation to the exact values. It is also important to note that you have to get rid of the 0th row and the 0th column of the co-occurrence matrix in order not to take into account the grey level used to flag non-ROI pixels.

To implement the workaround described above you just need to change the following two lines:

mancha = cruda[imaskc]
glcm = greycomatrix(mancha, [2], [0], levels=None, symmetric=True, normed=True)

to:

mancha = cruda.copy()
mancha[mancha == 0] = 1
mancha[~imaskc] = 0
glcm = greycomatrix(mancha, [2], [0], levels=None, symmetric=True, normed=True)[1:, 1:, :, :]
Tonechas
  • 13,398
  • 16
  • 46
  • 80