0

I would like to change the RGB values of an image according to a threshold. For example I would like a value in an image to become 0 if it's less than 0.5 and to become 1 if it's equal or greater than 0.5.

Here's an example: A pixel of values [0.3, 0.6, 0.9] should become [0, 1, 1]. I would like this to happen for ALL pixels. This would essentially make my image to use only 8 solid colors, which are the combinations of 0s and 1s in the RGB channel: namely pure red [1,0,0], pure green [0,1,0], pure yellow [1, 1, 0] , etc.

This is how I use the conditional:

from matplotlib import image


folder_dir = "..."
img = image.imread(folder_dir + "\\" + "my_image.png")
output = img.copy()
output[output >= 0.5] = 1
output[output < 0.5] = 0 

plt.imshow(output), plt.axis('off')

According to my knowledge this seems to work, but the areas between different colors in the output image seem to be covered with "mean-valued" colors, for example if there is a large area of red and a large area of white, then there is a slight pinkish line between them. The problem gets even more complicated with high frequency multi-colored images where new colors are emerging.

Both input and output images are PNG.

What am I doing wrong?

  • 1
    It is probably an artifact of `plt.imshow`. Some backends require `interpolation='nearest'`: `plt.imshow(output, interpolation='nearest')` – Rotem Sep 24 '22 at 08:31
  • @Rotem Thank you for your comment. What would you recommend instead (in order to save the output image) to avoid discolorations? – Amadeo Amadei Sep 25 '22 at 06:32
  • 1
    **1.** Save the image as PNG (not as JPEG). **2.** In case the image resolution is too large to fit the screen (or very small), the image viewer may downscale/upscale the image, and apply interpolation - view the image without scaling, or make sure the viewer use "Nearest Neighbor" interpolation. – Rotem Sep 25 '22 at 08:05
  • @Rotem Thank you so much for your comment once again! – Amadeo Amadei Sep 26 '22 at 13:53
  • @Rotem Using ```plt.imshow(output, interpolation='nearest')``` or ```plt.imshow(output, interpolation='none')``` solved the problem! – Amadeo Amadei Sep 26 '22 at 14:14

1 Answers1

1

Too much for a comment, and Rotem has already given you the answer, so more of an aside suggestion...

You don't need to make a copy of your input image, nor make two passes over it to determine on the first pass whether it is above or equal to your threshold and on the second if it is below.

You are essentially looking for a True/False, i.e. Boolean, as to whether your value is above/equal to the threshold or below it. So you can do:

img = image.imread(...)

# Produce True/False as whether above/equal threshold
output = img >= 0.5

As you want a floating point output, you can multiply the Boolean result by 1.0, so you'd do:

img = image.imread(...)
output = (img >= 0.5) * 1.0

So, no need to copy, and no need for two passes.


Here's a little example:

# Make repeatable randomness
np.random.seed(42)

# Create mini random image
im = np.random.rand(2,3)

print(im)

array([[0.37454012, 0.95071431, 0.73199394], [0.59865848, 0.15601864, 0.15599452]])

# Get thresholded image
print(im >= 0.5)

array([[False, True, True], [ True, False, False]])

# Get actual result you want
print((im >= 0.5) * 1.0)

array([[0., 1., 1.], [1., 0., 0.]])

Mark Setchell
  • 191,897
  • 31
  • 273
  • 432
  • Thank you for pointing out that I don't need the extra copy. As for the first part of your code, I don't quite get it, could elaborate a little bit more? – Amadeo Amadei Sep 26 '22 at 14:26
  • I am suggesting you completely replace your 3 lines starting with `output...` by the single line `output = (img >= 0.5) * 1.0` That saves your CPU going all through the array to find values above the threshold and then going all through it again to find the values below the threshold. – Mark Setchell Sep 26 '22 at 15:08
  • I've added an example to my answer, so you can see how it works. – Mark Setchell Sep 26 '22 at 15:44