0

Let’s say you have an image in the form of an 2D numpy array as the one below. Now you want to erode the image. However, you don’t want to erode it equally. It should be eroded only if the object in the image has an intensity < 50, otherwise it should not be affected at all.

In the image below, only the blue-marked area should be eroded but but the lighter gray area should be unchanged.

Furthermore, any dark-gray area adjacent to light-gray area should not be affected (red-marked). This latter requirement makes it harder to erode the whole object and then put back the original light gray area because such an operation could affect also the dark gray are marked in red if the erosion eats all the way to the red parts. In such case, there would be a hole in the object.

I have looked into OpenCV's erode function, but it does not have any options for this. I have also googled without finding any solution.

enter image description here

Christoph Rackwitz
  • 11,317
  • 4
  • 27
  • 36
Johan hvn
  • 318
  • 1
  • 2
  • 11
  • Does [Efficient thresholding filter of an array with numpy](https://stackoverflow.com/questions/7994394/efficient-thresholding-filter-of-an-array-with-numpy) answer your question? – wwii Aug 22 '22 at 16:35
  • Are your images always this simple, or is this just a simplified example? – Cris Luengo Aug 22 '22 at 17:11
  • See above, I added a description that makes it harder. – Johan hvn Aug 25 '22 at 18:12

1 Answers1

1

The straight-forward solution below might or might not work for more complex images. But for this example it should do what you need.

  1. Erode the whole image.
  2. Replace the regions you don’t want eroded with the original data.

For example (Python pseudo-code, using NumPy-style notation for indexing and so on):

out = erosion(img)
mask = img >= 50
out[mask] = img[mask]

This can also be written as

mask = out.copy()
mask[mask < 50] = 0
out = max(erosion(img), mask)

(where max is the element-wise maximum).


To avoid the erosion going "though" thin bright regions, use the smallest possible structuring element size in the erosion, and iterate:

out = img.copy()
for _ in range(10):
   out = max(erosion(out, size=1), mask)

This is of course slower, but it produces the effect you want.

Cris Luengo
  • 55,762
  • 10
  • 62
  • 120
  • Thanks for your replay. I have changed the image and description to also take into consideration a problem with the solution you suggested. Any ideas on how to extend your idea? – Johan hvn Aug 22 '22 at 18:53
  • @Johanhvn Yes, that makes it quite a bit more difficult. I’m sure there’s a solution, but it might require a custom filter. What image processing library are you using? – Cris Luengo Aug 22 '22 at 22:53
  • opencv, scikit-image, PIL or any other that works :) – Johan hvn Aug 23 '22 at 08:30
  • @Johanhvn I had forgotten about this! I've just updated the answer with a solution. – Cris Luengo Aug 26 '22 at 07:00