Alex S
Alex S

Reputation: 239

Altered image array when plotting with pyplot's imshow

So i noticed for me and a few of my colleagues that when we display a binary array using matplotlib.pyplot's imshow function the edges of the displayed image seems altered. For a while i thought it was just a visual artifact, but ran into further trouble with it today.

By the way i am running with matplotlib: 3.2.2 and numpy: 1.19.1

If i create a small binary array and plot it it you can see a small "halo" to the binary box in the image. It is not very obvious but it is there:

import matplotlib.pyplot as plt
import numpy as np

img=np.zeros((100,100))
img[25:60,25:60]=50
plt.imshow(img)

enter image description here

It will become more apparent if i change the cmap for the plot.

my_cmap = plt.cm.get_cmap('prism')
my_cmap.set_under('black')
plt.imshow(img,cmap=my_cmap, vmin=1)

enter image description here

The displayed array should only have 0's as background and 1's in the box, but the box is displayed as a green box with a red/yellow border.

With previous versions of pyplot i have not had this issue and it does become a problem when i do object detection and i want to display them and my other wise binary objects end up like this:

enter image description here

I hope you can help me with this

Upvotes: 0

Views: 1350

Answers (1)

JohanC
JohanC

Reputation: 80339

imshow doesn't know about your data being discrete or even binary. Default it adds some interpolation. You can suppress the smooth interpolation using imshow(...., interpolation='none') (or interpolation='nearest').

Note that the default mode is 'antialiased' for which the effect is different depending on the number of screen pixels occupied by an image pixel. See the official matplotlib documentation for more details.

Here is some test code comparing the default and the 'none' interpolation mode for different image sizes:

from matplotlib import pyplot as plt
import numpy as np

x = np.round(10 + np.random.uniform(-.1, .1, (100, 100)).cumsum(axis=0).cumsum(axis=1))
x[x % 2 == 0] = x.max() + 1
fig, axes = plt.subplots(2, 6, figsize=(14, 5))
for i, axrow in enumerate(axes):
    for j, ax in enumerate(axrow):
        k = 10 * (j + 5)
        ax.imshow(x[-k:, -k:], cmap='Dark2', interpolation=None if i == 0 else 'none')
        ax.set_title("size={}\ninterpolation={}".format(k, 'None' if i == 0 else "'none'"))
plt.tight_layout()
plt.show()

comparison

Here is another example, using the 'seismic' colormap and only two data values. This colormap has dark blue and red at the extremes and white near the center, which shows the interpolation much more pronunciated:

two data values, seismic cmap

Upvotes: 2

Related Questions