Search code examples
pythonopencvmatplotlibvideospyder

Importing, editing, and displaying videos in spyder, python? (alternative to cv2.imshow?)


I am making a script in which you read a video file and detect and track object motion. I am loosely following this methodology: https://pyimagesearch.com/2015/05/25/basic-motion-detection-and-tracking-with-python-and-opencv/

However, I don't want to run the code from a command window, I want it to work within spyder. This is a problem as at the end of the code, it displays the finished video using the cv2.imshow(frame) command, which instantly crashes spyder for some reason. To get around this, I am trying to use matplotlib instead, but I just can't get the frames to replace each other in one window (ie put the frames back together to form a video again).

This is the code I am using:

def cv2_imshow(a, **kwargs):
      a = a.clip(0, 255).astype('uint8')
      # cv2 stores colors as BGR; convert to RGB
      if a.ndim == 3:
          if a.shape[2] == 4:
              a = cv2.cvtColor(a, cv2.COLOR_BGRA2RGBA)
          else:
              a = cv2.cvtColor(a, cv2.COLOR_BGR2RGB)

      return plt.imshow(a, **kwargs)  
    
    get_ipython().run_line_magic('matplotlib', 'qt')
    plt.ion()
    cv2_imshow(frame)

What I end up with is basically a new window being created for each frame all over my screen (ie it is not a video in one window)

Does anyone have a way of doing this?

Essentially I want the process to be this: read video --> detect motion, create frame with threshold and frame with moving object in red box --> repeat over all frames, creating 3 videos (or even just the finished video with the movement detection)


Solution

  • According to this post, it is not possible to update the "inline plots" in Spyder.

    The closet solution I found is using clear_output as described in this post.

    Code sample:

    import cv2
    import numpy as np
    import matplotlib.pyplot as plt
    from IPython.display import clear_output
    
    width, height, n_frames, fps = 320, 240, 10, 1
    
    
    def cv2_imshow(a, **kwargs):
        a = a.clip(0, 255).astype('uint8')
        # cv2 stores colors as BGR; convert to RGB
        if a.ndim == 3:
            if a.shape[2] == 4:
                a = cv2.cvtColor(a, cv2.COLOR_BGRA2RGBA)
            else:
                a = cv2.cvtColor(a, cv2.COLOR_BGR2RGB)
    
        # https://matplotlib.org/stable/gallery/showcase/mandelbrot.html#sphx-glr-gallery-showcase-mandelbrot-py
        dpi = 72
        width, height = a.shape[1], a.shape[0]
        fig = plt.figure(figsize=(width/dpi, height/dpi), dpi=dpi)  # Create new figure
        ax = fig.add_axes([0, 0, 1, 1], frameon=False, aspect=1)  # Add axes to figure
        ax.imshow(a, **kwargs)
        plt.axis('off')
        plt.show(block=False)  # Show image without "blocking"        
    
    
    def make_image(i):
        """ Build synthetic BGR image for testing """
        p = width//60
        im = np.full((height, width, 3), 60, np.uint8)
        cv2.putText(im, str(i+1), (width//2-p*10*len(str(i+1)), height//2+p*10), cv2.FONT_HERSHEY_DUPLEX, p, (255, 30, 30), p*2)  # Blue number
        return im
    
    
    # Show synthetic images in a loop
    for i in range(n_frames):
        a = make_image(i)
        cv2_imshow(a)
        plt.pause(1/fps)
    
        # https://stackoverflow.com/a/59736741/4926757    
        clear_output(wait=False)