Brent
Brent

Reputation: 729

Update greyscale image with matplotlib

I have a series of images coming from a video stream that I would like to display with Matplotlib (in greyscale). For some reason I can get them to display in colour perfectly fine, but not when I convert them to greyscale.

Since I have a lot of images, I am using set_data( image ) instead of imshow( image ) as it is much faster. Colour images work fine with both of these commands, but greyscale only works with imshow(). If I use set_data() I just get a black image, no matter what data I sent it.

Any idea what could be going on here?

My code is below for reference. I've highlighted the important bit with a big comment, but I've included all of my other code in case something else is causing the issue. The rest of the code basically sets up a queue which is populated with images from a camera and I want to display them as I get them.

import matplotlib.pyplot as plt
from matplotlib import cm
from cv_bridge import CvBridge, CvBridgeError
import cv
from collections import deque
import rospy
from sensor_msgs.msg import Image
import numpy as np

class CameraViewer():

  def __init__( self, root='navbot' ):

    self.root = root
    self.im_data = deque()
    self.bridge = CvBridge() # For converting ROS images into a readable format

    self.im_fig = plt.figure( 1 )
    self.im_ax = self.im_fig.add_subplot(111)
    self.im_ax.set_title("DVS Image")
    self.im_im = self.im_ax.imshow( np.zeros( ( 256, 256 ),dtype='uint8' ), cmap=plt.cm.gray ) # Blank starting image
    #self.im_im = self.im_ax.imshow( np.zeros( ( 256, 256 ),dtype='float32' ), cmap=plt.cm.gray ) # Tried a different format, also didn't work
    self.im_fig.show()
    self.im_im.axes.figure.canvas.draw()

  def im_callback( self, data ):

    cv_im = self.bridge.imgmsg_to_cv( data, "mono8" ) # Convert Image from ROS Message to greyscale CV Image
    im = np.asarray( cv_im ) # Convert from CV image to numpy array
    #im = np.asarray( cv_im, dtype='float32' ) / 256 # Tried a different format, also didn't work
    self.im_data.append( im )

  def run( self ):

    rospy.init_node('camera_viewer', anonymous=True)

    sub_im = rospy.Subscriber( self.root + '/camera/image', Image, self.im_callback)

    while not rospy.is_shutdown():
      if self.im_data:
        im = self.im_data.popleft()

        #######################################################
        # The following code is supposed to display the image:
        #######################################################

        self.im_im.set_cmap( 'gray' ) # This doesn't seem to do anything
        self.im_im.set_data( im ) # This won't show greyscale images
        #self.im_ax.imshow( im, cmap=plt.cm.gray ) # If I use this, the code runs unbearably slow
        self.im_im.axes.figure.canvas.draw()

def main():

  viewer = CameraViewer( root='navbot' )
  viewer.run()

if __name__ == '__main__':
  main()

Upvotes: 2

Views: 986

Answers (1)

Joe Kington
Joe Kington

Reputation: 284642

What's happening is that the min and max of the data are changing. im.set_data just blindly sets the data -- it doesn't try to update the color scaling.

Initially, you plot an array of all zeros, so imshow scales the image between 0 and 0 (plus a tiny fudge factor). However, the data you update it with has a range between 0 and 255.

You'll need to either a) set the vmin and vmax of the data to the maximum range when you initially plot the image, or b) update the color limits with the min and max of the data each time you update the image (use im.set_clim).

Because you seem to be expecting the data to be uint8's, it's probably best to just specify vmin and vmax when you initially plot your blank image.

Try doing:

# Blank starting image
self.im_im = self.im_ax.imshow(np.zeros((256, 256)), cmap=plt.cm.gray, 
                               vmin=0, vmax=255) 

Upvotes: 8

Related Questions