Change pixel colors of an image plt.imshow ()

I need to draw an image using matplotlib imshow () and then mark several pixels with different colors. Just changing their value in the initial array will not work as I need to use colors that are not present in the colourmap I am using. So my original intention was to build the second generated array over the first image, with most of it masked and the required pixels unmasked and with some values ​​(potentially different - to use a different color for different coordinates). And it works fine with the interactive matplotlib viewer, but when saved to file, everything is garbled, probably due to this bug I reported in the same situation: https://github.com/matplotlib/matplotlib/issues/3057

Are there any other options for changing the color of some pixels?

+3


source to share


1 answer


You've already suggested the easiest way to do this (overlaying another image on top), but if that doesn't work the way you want, there are other options.


Approach # 1 - Manually Render and Compose the Image


The most direct way is to just map your array to RGB using a color map and then change the pixels you want.

As a quick example:

import numpy as np
import matplotlib.pyplot as plt

data = np.arange(100).reshape(10, 10)

cmap = plt.cm.gray
norm = plt.Normalize(data.min(), data.max())
rgba = cmap(norm(data))

# Set the diagonal to red...
rgba[range(10), range(10), :3] = 1, 0, 0

plt.imshow(rgba, interpolation='nearest')
plt.show()

      

enter image description here

The downside to this method is that you cannot just call fig.colorbar(im)

as you are passing in a pre-rendered rgb image. So if you want a color bar you will have to use a proxy artist. The easiest way is to add an additional invisible (not drawn, not transparent) artist imshow(data, visible=False)

, and then base that artist's color code. As a quick example:

import numpy as np
import matplotlib.pyplot as plt

data = np.arange(100).reshape(10, 10)

cmap = plt.cm.gray
norm = plt.Normalize(data.min(), data.max())
rgba = cmap(norm(data))

# Set the diagonal to red
rgba[range(10), range(10), :3] = 1, 0, 0

fig, ax = plt.subplots()
ax.imshow(rgba, interpolation='nearest')

# Add the colorbar using a fake (not shown) image.
im = ax.imshow(data, visible=False, cmap=cmap)
fig.colorbar(im)

plt.show()

      

enter image description here

Using invisible imshow

is the easiest way to make a proxy executor for this purpose, but if speed is an issue (or if it somehow caused the rendering error you mentioned), you can use either one as well ScalarMappable

. ScalarMappable

is an abstract base class commonly used for inheritance to support colorbar. Since we don't need to draw anything, we can just use it directly.

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.cm import ScalarMappable

data = np.arange(100).reshape(10, 10)

cmap = plt.cm.gray
norm = plt.Normalize(data.min(), data.max())
rgba = cmap(norm(data))

# Set the diagonal to red
rgba[range(10), range(10), :3] = 1, 0, 0

fig, ax = plt.subplots()
ax.imshow(rgba, interpolation='nearest')

# Add the colorbar using a ScalarMappable
im = ScalarMappable(norm, cmap)
im.set_array(data)
fig.colorbar(im)

plt.show()

      




Approach # 2 - Abuse set_bad

, set_over

orset_under


set_bad

, set_over

and set_under

color-map methods that let you mark pixels that are NaN or outside a specified color-map range.

So another way to do what you want is to set these values ​​to NaN and specify what the NaN color should be ( set_bad

.. By default, it is transparent to most color palettes.).

If you have an integer array or already need to have transparent NaN pixels, you can also abuse set_over

and set_under

. In this case, you will need to manually specify vmin

or vmax

when called imshow

.

As a quick example of use / abuse set_bad

for this:

import numpy as np
import matplotlib.pyplot as plt

data = np.arange(100).reshape(10, 10).astype(float)

cmap = plt.cm.gray
cmap.set_bad((1, 0, 0, 1))

# Set the diagonal to NaN
data[range(10), range(10)] = np.nan

plt.imshow(data, cmap=cmap, interpolation='nearest')
plt.show()

      

enter image description here

One of the advantages of this method over the first is that it is a little easier to draw. (The downside is that this method is much less flexible.):

import numpy as np
import matplotlib.pyplot as plt

data = np.arange(100).reshape(10, 10).astype(float)

cmap = plt.cm.gray
cmap.set_bad((1, 0, 0, 1))

# Set the diagonal to NaN
data[range(10), range(10)] = np.nan

plt.imshow(data, cmap=cmap, interpolation='nearest')
plt.colorbar()
plt.show()

      

enter image description here

+6


source







All Articles