Home > Blockchain >  Animating plot image subplots synchronously in matplotlib
Animating plot image subplots synchronously in matplotlib

Time:12-21

I have a figure that contains both a curve plot and a corresponding image. I'd like for the two of them to shift in sync with one another -- so that the white region of the image follows the location in the curve where all three curves match up. (For the curious, this is intended as a simple simulation of a multiwavelength interferogram.)

The three curves in the plot are copied point-by-point into the into the R, G, and B channels of the image frame. As a result, I expected the two to be naturally in sync. However, in the animation one can see that the curves shift at a faster rate than the colors in the image.

After trying a number of adjustments (aspect ratio of the image, changing the image "extent", etc.) I have so far failed to locate the problem.

from numpy import *
import matplotlib.pyplot as plt
from matplotlib import animation

npts = 501
wavelength_blue = 0.45
wavelength_green = 0.55
wavelength_red = 0.65
z = 1.5 * linspace(-0.75, 0.75, npts)      ## distance in microns
nframes = 100
maxshift = 0.55     ## in microns of distance
img_height = 100
img = 255 * ones((img_height,npts,3), 'uint8')

(fig,axes) = plt.subplots(2, num='propagation_phase_shifted')
p1 = axes[0].plot([], [], 'b-')
p2 = axes[0].plot([], [], 'g-')
p3 = axes[0].plot([], [], 'r-')
axes[0].set_xlim((-0.8,0.8))
axes[0].set_ylim((-0.1,1.1))
p4 = axes[1].imshow(img, extent=[-0.8,0.8,-0.1,1.1], aspect=1/3)

axes[0].xaxis.set_label_position('top')
axes[0].xaxis.set_ticks_position('top')
axes[0].set_xlabel('z-distance (um)')
axes[0].set_ylabel('wave amplitude')
axes[0].tick_params(axis='x', direction='out')
plt.subplots_adjust(hspace=0.05)

def animate(n):
    shift = (n / (nframes - 1.0)) * maxshift
    phi_red = 2.0 * pi * (z-shift) / wavelength_red
    phi_green = 2.0 * pi * (z-shift) / wavelength_green
    phi_blue = 2.0 * pi * (z-shift) / wavelength_blue

    y_red = 0.5 * (1.0   cos(phi_red))
    y_green = 0.5 * (1.0   cos(phi_green))
    y_blue = 0.5 * (1.0   cos(phi_blue))

    for x in range(img_height):
        img[x,:,0] = uint8(255 * y_red)
        img[x,:,1] = uint8(255 * y_green)
        img[x,:,2] = uint8(255 * y_blue)

    p1[0].set_data(z, y_red)
    p2[0].set_data(z, y_green)
    p3[0].set_data(z, y_blue)
    p4.set_data(img)
    return(p1[0],p2[0],p3[0],p4)

anim = animation.FuncAnimation(fig, animate, frames=nframes, interval=20, blit=True)
FFwriter = animation.FFMpegWriter(fps=30)
anim.save('result.mp4', writer=FFwriter)

plt.show()

enter image description here

CodePudding user response:

I think the problem occurs when you convert your distance in microns. The same data is stretched between -1.125..1.125 on the top figure (1.5*0.75=1.125) and -0.8..0.8 on the bottom one. The top one then appear to move faster.

Fixing the intervals should work. Also if I'm not mistaken, you interverted red and blue in the top plot:

from numpy import *
import matplotlib.pyplot as plt
from matplotlib import animation

npts = 501
wavelength_blue = 0.45
wavelength_green = 0.55
wavelength_red = 0.65
z = 1.5 * linspace(-0.75, 0.75, npts)      ## distance in microns
nframes = 100
maxshift = 0.55     ## in microns of distance
img_height = 100
img = 255 * ones((img_height,npts,3), 'uint8')

(fig,axes) = plt.subplots(2, num='propagation_phase_shifted')
p1 = axes[0].plot([], [], 'r-')     # <--------- replacing b with r
p2 = axes[0].plot([], [], 'g-')
p3 = axes[0].plot([], [], 'b-')     # <--------- replacing r with b
axes[0].set_xlim((-1.125,1.125))    # <--------- fixing interval
axes[0].set_ylim((-0.1,1.1))
p4 = axes[1].imshow(img, extent=[-1.125,1.125,-0.1,1.1], aspect=1/3)    # <--------- fixing interval

axes[0].xaxis.set_label_position('top')
axes[0].xaxis.set_ticks_position('top')
axes[0].set_xlabel('z-distance (um)')
axes[0].set_ylabel('wave amplitude')
axes[0].tick_params(axis='x', direction='out')
plt.subplots_adjust(hspace=0.05)

def animate(n):
    shift = (n / (nframes - 1.0)) * maxshift
    phi_red = 2.0 * pi * (z-shift) / wavelength_red
    phi_green = 2.0 * pi * (z-shift) / wavelength_green
    phi_blue = 2.0 * pi * (z-shift) / wavelength_blue

    y_red = 0.5 * (1.0   cos(phi_red))
    y_green = 0.5 * (1.0   cos(phi_green))
    y_blue = 0.5 * (1.0   cos(phi_blue))

    for x in range(img_height):
        img[x,:,0] = uint8(255 * y_red)
        img[x,:,1] = uint8(255 * y_green)
        img[x,:,2] = uint8(255 * y_blue)

    p1[0].set_data(z, y_red)
    p2[0].set_data(z, y_green)
    p3[0].set_data(z, y_blue)
    p4.set_data(img)
    return(p1[0],p2[0],p3[0],p4)

anim = animation.FuncAnimation(fig, animate, frames=nframes, interval=20, blit=True)
FFwriter = animation.FFMpegWriter(fps=30)
anim.save('result.mp4', writer=FFwriter)

plt.show()
  • Related