Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Movie is showing up as binarized, and resizing parts (depending on dtype) #38

Closed
EricThomson opened this issue Nov 5, 2022 · 2 comments

Comments

@EricThomson
Copy link
Contributor

EricThomson commented Nov 5, 2022

I'm generating a movie of synthetic calcium data (either as uint8 or float), in Windows 10 (note: subsequently confirmed on Ubuntu so this doesn't seem to be an OS-specific thing), and things are looking weird. Note code examples to reproduce are all below.

I roughly expect it to look like this (created using opencv, saved from uint8):

opencv_save.mp4

When a component is activated, it is bright, and then activity slowly decays. And each component is a 2d Gaussian, so has a bright center that gradually falls off as you move off from its center.

Using fastplotlib, the way it looks when I view with slider as original float data:

recreate_float.mp4

The way it looks as uint8 from fastplotlib:

recreate_uint8.mp4

This is better in the sense of no strange balloon artifacts, but still binarized (note it's qualitatively the same when cmap is gray, but I used viridis here it doesn't matter for the effect).

I've confirmed this on another Windows machine, and same behavior on Ubuntu 20.04.

Code

Create movie array

import numpy as np
import matplotlib.pyplot as plt
from scipy.stats import multivariate_normal

# set up gaussians centered at component_centers
num_frames = 1000
image_size = 200
frame_shape = np.array([image_size, image_size])
component_centers = np.array([[40, 40],
                              [110, 90],
                              [180, 55],
                              [60, 135],
                              [140, 140]]);
num_components = component_centers.shape[0]

# create component images: stack of images one for ech component
sigma = 20
x, y = np.meshgrid(np.arange(0, image_size), 
                   np.arange(0, image_size)) 
pos = np.dstack((x, y))
component_sigma = np.array([[sigma, 0],[0, sigma]])
component_images = []
for component_num in np.arange(num_components):
    component_mean = component_centers[component_num]
    gauss_rep = multivariate_normal(component_mean, component_sigma)
    gauss_img = gauss_rep.pdf(pos)
    component_images.append(gauss_img)
component_images = np.array(component_images)


# generate traces
tau = 10
max_amp = 255  # just to make saving as uint8 in opencv simple
amps_all = []

for component_num in np.arange(num_components):
    amps = []
    amp = 0
    for time_step in np.arange(num_frames):
        if np.random.uniform(0,1) > 0.98:
            amp = max_amp
        else:
            amp = np.max(np.array([amp - amp/tau, 0]));
        amps.append(amp)
    amps = np.array(amps)
    amps_all.append(amps)
amps_all = np.array(amps_all)

# create movie
movie = np.zeros((num_frames, image_size, image_size))
for frame_num in np.arange(num_frames):
    component0 = amps_all[0][frame_num]*component_images[0]
    component1 = amps_all[1][frame_num]*component_images[1]
    component2 = amps_all[2][frame_num]*component_images[2]
    component3 = amps_all[3][frame_num]*component_images[3]
    component4 = amps_all[4][frame_num]*component_images[4]
    movie[frame_num] = component0 + component1 + component2 + component3 + component4 

Show in fastplotlib (as float)

plot = Plot()
image = plot.image(data=movie[0], cmap='viridis')

movie_slider = IntSlider(value=0, min=0, max=movie.shape[0]-1, step=1) 

previous_index = 0

def update_frame():
    global previous_index
    if movie_slider.value == previous_index:
        return
    image.update_data(data=movie[movie_slider.value])
    
plot.add_animations([update_frame])
VBox([plot.show(), movie_slider])

Save using opencv

import cv2
# convert to uint8
movie2 = movie.copy()
movie2 = movie2 / np.max(movie2) # normalize the data to 1 max
movie2 = 255* movie2
movie2 = movie2.astype(np.uint8)
fps = 25
out = cv2.VideoWriter('synthetic_ca.mp4', 
                      cv2.VideoWriter_fourcc(*'DIVX'),  #mp4v (encoding)
                      fps, 
                      (image_size, image_size), 
                      False) # is_color
for frame in movie2:
    out.write(frame)
out.release()

Show uint8 version in fastplotlib

plot2 = Plot()
image2 = plot2.image(data=movie2[0], cmap='viridis')

movie_slider2 = IntSlider(value=0, min=0, max=movie2.shape[0]-1, step=1) 

previous_index = 0

def update_frame2():
    global previous_index
    if movie_slider2.value == previous_index:
        return
    image2.update_data(data=movie2[movie_slider2.value])
    
plot2.add_animations([update_frame2])
VBox([plot2.show(), movie_slider2])
@kushalkolar
Copy link
Member

kushalkolar commented Nov 17, 2022

@EricThomson sorry for the late response, this is just a vmin vmax thing :D

Since the first frame's min and max is zero, everything just appears extreme when you slider through the subsequent frames.

If you instead use this it works:

image = plot.image(
    data=movie[0],
    cmap='viridis',
    vmin=movie.min(),
    vmax=movie.max(),
)

@EricThomson
Copy link
Contributor Author

Yes this fixes everything thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants