soft-matter / trackpy

Python particle tracking toolkit
http://soft-matter.github.io/trackpy
Other
441 stars 131 forks source link

Writing annoted TIFF stack to file without `matplotlib` frame #702

Closed jacopoabramo closed 1 year ago

jacopoabramo commented 2 years ago

Greetings,

I'm trying to call trackpy.annotate after detecting the particles on my video recording. What I want to achieve is to annotate the centroids on each image, completely remove the matplotlib background and axis and store the annotated image on a contigous TIFF file. I've come up with a working snippet which is an hack from issue #167:

import matplotlib.pyplot as plt
import numpy as np
import trackpy as tp
import tifffile as tif
import warnings
import pims
from tqdm import tqdm

video = pims.open("./test_data/event2_MF_avF.tif")
df_file = tp.PandasHDFStoreBig("./datasets/particle_data_positions.h5", mode="r")
dataframe = df_file.dump()

plot_style = dict(markersize=12, markeredgewidth=0,
                       markerfacecolor='k', markeredgecolor='none',
                       marker='.')

# plt.ioff() disable figure pop up when generating
with plt.ioff(), warnings.catch_warnings(), tqdm(range(len(video)), desc="video drawing") as pbar, tif.TiffWriter("test_video_drawn.tif") as outfile:
    warnings.simplefilter("ignore")
    for idx, frame in enumerate(video):
        fig, ax = plt.subplots()
        tp.annotate(dataframe.query(f'frame=={idx}'), frame, ax=ax, plot_style=plot_style)
        ax.set(yticks=[], xticks=[])
        fig.tight_layout(pad=0)
        fig.canvas.draw()
        data = np.array(fig.canvas.buffer_rgba())
        data = np.rint(data[...,:3] @ [0.2126, 0.7152, 0.0722]).astype(np.uint16)
        plt.close("all")
        pbar.update(1)
        outfile.write(data, contiguous=True)

This script works, but what I get is the following output:

image

As you can see the actual video is still surrounded by the white background of the generated figure. I want to get rid of it but don't know exactly how to approach this. It would also be great if trackpy.annotate had an option to return the annotate image without the matplotlib figure axes and background.

jacopoabramo commented 2 years ago

After some more tweeks I found a way to do it as follows:

range = range(int(len(video)))
max = int(len(video))

with plt.ioff(), warnings.catch_warnings(), tqdm(range, desc="video drawing") as pbar, tif.TiffWriter("test_video_drawn.tif") as outfile:
    warnings.simplefilter("ignore")
    for idx, frame in enumerate(video):  # show every 50th frame to keep file size low
        fig_kw = dict(frameon=False, figsize=(frame.shape[0]/20, frame.shape[1]/20), dpi=20)
        fig, ax = plt.subplots(**fig_kw)
        tp.annotate(dataframe.query(f'frame=={idx}'), frame, ax=ax, plot_style=plot_style)
        ax.set(yticks=[], xticks=[])
        plt.axis('off') # this rows the rectangular frame 
        ax.get_xaxis().set_visible(False) # this removes the ticks and numbers for x axis
        ax.get_yaxis().set_visible(False) # this removes the ticks and numbers for y axis
        fig.tight_layout(pad=0)
        fig.canvas.draw()
        data = np.array(fig.canvas.buffer_rgba())
        data = np.rint(data[...,:3] @ [0.2126, 0.7152, 0.0722]).astype(np.uint16)
        plt.close("all")
        pbar.update(1)
        outfile.write(data, contiguous=True)
nkeim commented 1 year ago

This is great! Thanks. I'm closing this for now but I agree that it would be a welcome pull request as an option to annotate.