# HG changeset patch # User iuc # Date 1606571134 0 # Node ID 76733d05d8ef25433e3e129883e8e337b38be36d "planemo upload for repository https://github.com/galaxyproject/tools-iuc/tree/master/packages/spyboat commit 5c60a414c785246371beac23ce52d8bbab3602d1" diff -r 000000000000 -r 76733d05d8ef output_report.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/output_report.py Sat Nov 28 13:45:34 2020 +0000 @@ -0,0 +1,253 @@ +""" Produces plots and a summary html 'headless' """ +import logging +import os + +import matplotlib +import matplotlib.pyplot as ppl +import spyboat.plotting as spyplot + +ppl.switch_backend('Agg') +matplotlib.rcParams["text.usetex"] = False +logger = logging.getLogger(__name__) + +# figure resolution +DPI = 250 + + +def produce_snapshots(input_movie, results, frame, Wkwargs, img_path="."): + """ + Takes the *input_movie* and the *results* dictionary + from spyboat.processing.run_parallel and produces phase, + period and amplitude snapshot png's. + + For the period snapshot also the period range is needed, + hence the analysis dictionary 'Wkwargs' also gets passed. + + The output files name pattern is: + [input, phase, period, amplitude]_frame{frame}.png + and the storage location in *img_path*. + + These get picked up by 'create_html' + """ + + spyplot.input_snapshot(input_movie[frame]) + fig = ppl.gcf() + out_path = os.path.join(img_path, f"input_frame{frame}.png") + fig.savefig(out_path, dpi=DPI) + ppl.close(fig) + + spyplot.phase_snapshot(results["phase"][frame]) + fig = ppl.gcf() + out_path = os.path.join(img_path, f"phase_frame{frame}.png") + fig.savefig(out_path, dpi=DPI) + ppl.close(fig) + + spyplot.period_snapshot( + results["period"][frame], Wkwargs["Tmin"], + Wkwargs["Tmax"], time_unit="a.u." + ) + + fig = ppl.gcf() + out_path = os.path.join(img_path, f"period_frame{frame}.png") + fig.savefig(out_path, dpi=DPI) + ppl.close(fig) + + spyplot.amplitude_snapshot(results["amplitude"][frame]) + fig = ppl.gcf() + out_path = os.path.join(img_path, f"amplitude_frame{frame}.png") + fig.savefig(out_path, dpi=DPI) + ppl.close(fig) + + logger.info(f"Produced 4 snapshots for frame {frame}..") + + +def produce_distr_plots(results, Wkwargs, img_path="."): + """ + Output file names are: + + period_distr.png, power_distr.png and phase_distr.png + """ + + spyplot.period_distr_dynamics(results["period"], Wkwargs) + fig = ppl.gcf() + out_path = os.path.join(img_path, "period_distr.png") + fig.savefig(out_path, dpi=DPI) + + spyplot.power_distr_dynamics(results["power"], Wkwargs) + fig = ppl.gcf() + out_path = os.path.join(img_path, "power_distr.png") + fig.savefig(out_path, dpi=DPI) + + spyplot.phase_coherence_dynamics(results["phase"], Wkwargs) + fig = ppl.gcf() + out_path = os.path.join(img_path, "phase_distr.png") + fig.savefig(out_path, dpi=DPI) + + logger.info("Produced 3 distribution plots..") + + +def create_html(frame_nums, html_fname="OutputReport.html"): + """ + The html generated assumes the respective png's + have been created with 'produce_snapshots' and 'produce_distr_plots' + and can be found at the cwd (that's how Galaxy works..) + """ + + # -- create a gallery for every frame in frame_nums -- + + galleries = "" + for frame_num in frame_nums: + new_gal = f""" +
+

+ Frame Nr. {frame_num}

+ + +
+ """ + galleries += new_gal + + html_string = f""" + + + SpyBOAT Output Report + + + + + + +

SpyBOAT Results Report

+
+

Distribution Dynamics

+ + +

Output Movie Snapshots

+ + +
+ + +
+ + + {galleries} + + + + + + + """ + + with open(html_fname, "w") as OUT: + OUT.write(html_string) + + logger.info("Created html report") + return html_string + +# for local testing +# create_html([0,20,40,60,80]) diff -r 000000000000 -r 76733d05d8ef run_tests.sh --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/run_tests.sh Sat Nov 28 13:45:34 2020 +0000 @@ -0,0 +1,15 @@ +#!/usr/bin/env bash + + +INPUT_PATH='./test-data/test-movie.tif' +SCRIPT_PATH='.' + +# set to galaxy defaults!! +python3 $SCRIPT_PATH/spyboat_cli.py --input_path $INPUT_PATH --phase_out phase_out.tif --period_out period_out.tif --dt 1 --Tmin 20 --Tmax 30 --nT 150 --ncpu 6 --Tcutoff 40 + +# additional paramters +#--masking static --mask_frame 10 --mask_thresh 300 --rescale 80 --gauss_sigma 1 + +printf "\n" +# printf "\nError examples:\n" + diff -r 000000000000 -r 76733d05d8ef spyboat.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/spyboat.xml Sat Nov 28 13:45:34 2020 +0000 @@ -0,0 +1,264 @@ + +wavelet transforms image stacks + + 0.1.1 + + + spyboat + + python '$__tool_directory__/spyboat_cli.py' --version + /dev/null && + ## run tool + python '$__tool_directory__/spyboat_cli.py' + --input_path '$movie' + #if str($PreProc.gauss_sigma): + --gauss_sigma $PreProc.gauss_sigma + #end if + #if str($PreProc.rescale_factor): + --rescale $PreProc.rescale_factor + #end if + + #if $masking.selection_mode == "create_static_mask": + --masking static + --mask_frame $masking.mask_frame + --mask_thresh $masking.mask_thresh + #else if $masking.selection_mode == "create_dynamic_mask": + --masking dynamic + --mask_thresh $masking.mask_thresh + #end if + + --dt $wavana.dt + --Tmin $wavana.tmin + --Tmax $wavana.tmax + --nT $wavana.nt + #if str($wavana.tcutoff) + --Tcutoff $wavana.tcutoff + #end if + #if str($wavana.win_size): + --win_size $wavana.win_size + #end if + + #if 'save_phase' in $outputs: + --phase_out '$phase_out' + #end if + + #if 'save_period' in $outputs: + --period_out '$period_out' + #end if + + #if 'save_power' in $outputs: + --power_out '$power_out' + #end if + + #if 'save_amplitude' in $outputs: + --amplitude_out '$amplitude_out' + #end if + + #if 'save_preprocessed' in $outputs: + --preprocessed_out '$preprocessed_out' + #end if + + --html_fname '$html_out' + --report_img_path '$html_out.extra_files_path' + + --ncpu "\${GALAXY_SLOTS:-8}" + + + ]]> + + + + +
+ + +
+
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + +
+ + + + 'save_phase' in ouputs + + + 'save_period' in outputs + + + 'save_power' in outputs + + + 'save_amplitude' in outputs + + + 'save_preprocessed' in outputs + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + + + + + +
+ +
+ + + +
+ + + + + + + + + + + +
+
+ + + 10.1101/2020.04.29.067744 + +
diff -r 000000000000 -r 76733d05d8ef spyboat_cli.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/spyboat_cli.py Sat Nov 28 13:45:34 2020 +0000 @@ -0,0 +1,217 @@ +#!/usr/bin/env python + +# Gets interfaced by Galaxy or can be used for bash scripting +import argparse +import logging +import os +import sys + +import output_report +import spyboat +from numpy import float32 +from skimage import io + +logging.basicConfig(level=logging.INFO, stream=sys.stdout, force=True) +logger = logging.getLogger('spyboat-cli') + +# ----------command line parameters --------------- + +parser = argparse.ArgumentParser(description='Process some arguments.') + +# I/O +parser.add_argument('--input_path', help="Input movie location", required=True) +parser.add_argument('--phase_out', help='Phase output file name', required=False) +parser.add_argument('--period_out', help='Period output file name', required=False) +parser.add_argument('--power_out', help='Power output file name', required=False) +parser.add_argument('--amplitude_out', help='Amplitude output file name', required=False) +parser.add_argument('--preprocessed_out', help="Preprocessed-input output file name", required=False) + +# (Optional) Multiprocessing + +parser.add_argument('--ncpu', help='Number of processors to use', + required=False, type=int, default=1) + +# Optional spatial downsampling +parser.add_argument('--rescale_factor', help='Rescale the image by a factor given in %%, None means no rescaling', + required=False, type=int) +# Optional Gaussian smoothing +parser.add_argument('--gauss_sigma', help='Gaussian smoothing parameter, None means no smoothing', required=False, + type=float) + +# Wavelet Analysis Parameters +parser.add_argument('--dt', help='Sampling interval', required=True, type=float) +parser.add_argument('--Tmin', help='Smallest period', required=True, type=float) +parser.add_argument('--Tmax', help='Biggest period', required=True, type=float) +parser.add_argument('--nT', help='Number of periods to scan for', required=True, type=int) + +parser.add_argument('--Tcutoff', help='Sinc cut-off period, disables detrending if not set', required=False, type=float) +parser.add_argument('--win_size', help='Sliding window size for amplitude normalization, None means no normalization', + required=False, type=float) + +# Optional masking +parser.add_argument('--masking', help="Set to either 'dynamic', 'static' or 'None' which is the default", + default='None', required=False, type=str) + +parser.add_argument('--mask_frame', + help="The frame of the input movie to create a static mask from, needs masking set to 'static'", + required=False, type=int) + +parser.add_argument('--mask_thresh', + help='The threshold of the mask, all pixels with less than this value get masked (if masking enabled).', + required=False, type=float, + default=0) + +# output html report/snapshots +parser.add_argument('--html_fname', help="Name of the html report.", + default='OutputReport.html', required=False, type=str) + +parser.add_argument('--report_img_path', help="For the html report, to be set in Galaxy. Without galaxy leave at cwd!", + default='.', required=False, type=str) + +parser.add_argument('--version', action='version', version='0.1.0') + +arguments = parser.parse_args() + +logger.info("Received following arguments:") +for arg in vars(arguments): + logger.info(f'{arg} -> {getattr(arguments, arg)}') + +# ------------Read the input---------------------------------------- +try: + movie = spyboat.open_tif(arguments.input_path) +except FileNotFoundError: + logger.critical(f"Couldn't open {arguments.input_path}, check movie storage directory!") + sys.exit(1) +# problems get logged in 'open_tif' +if movie is None: + sys.exit(1) +# -------- Do (optional) spatial downsampling --------------------------- + +scale_factor = arguments.rescale_factor + +# defaults to None +if not scale_factor: + logger.info('No downsampling requested..') + +elif 0 < scale_factor < 100: + logger.info(f'Downsampling the movie to {scale_factor:d}% of its original size..') + movie = spyboat.down_sample(movie, scale_factor / 100) +else: + raise ValueError('Scale factor must be between 0 and 100!') + +# -------- Do (optional) pre-smoothing ------------------------- +# note that downsampling already is a smoothing operation.. + +# check if pre-smoothing requested +if not arguments.gauss_sigma: + logger.info('No pre-smoothing requested..') +else: + logger.info(f'Pre-smoothing the movie with Gaussians, sigma = {arguments.gauss_sigma:.2f}..') + + movie = spyboat.gaussian_blur(movie, arguments.gauss_sigma) + +# ----- Set up Masking before processing ---- + +mask = None +if arguments.masking == 'static': + if not arguments.mask_frame: + logger.critical("Frame number for static masking is missing!") + sys.exit(1) + + if (arguments.mask_frame > movie.shape[0]) or (arguments.mask_frame < 0): + logger.critical(f'Requested frame does not exist, input only has {movie.shape[0]} frames.. exiting') + sys.exit(1) + + else: + logger.info(f'Creating static mask from frame {arguments.mask_frame} with threshold {arguments.mask_thresh}') + mask = spyboat.create_static_mask(movie, arguments.mask_frame, + arguments.mask_thresh) +elif arguments.masking == 'dynamic': + logger.info(f'Creating dynamic mask with threshold {arguments.mask_thresh}') + mask = spyboat.create_dynamic_mask(movie, arguments.mask_thresh) + +else: + logger.info('No masking requested..') + +# ------ Retrieve wavelet parameters --------------------------- + +Wkwargs = {'dt': arguments.dt, + 'Tmin': arguments.Tmin, + 'Tmax': arguments.Tmax, + 'nT': arguments.nT, + 'T_c': arguments.Tcutoff, # defaults to None + 'win_size': arguments.win_size # defaults to None + } + +# --- start parallel processing --- + +results = spyboat.run_parallel(movie, arguments.ncpu, **Wkwargs) + +# --- masking? --- + +if mask is not None: + # mask all output movies (in place!) + for key in results: + logger.info(f'Masking {key}') + spyboat.apply_mask(results[key], mask, fill_value=-1) + +# --- Produce Output HTML Report Figures/png's --- + +# create the directory, yes we have to do that ourselves :) +# galaxy then magically renders the html from that directory +try: + + if arguments.report_img_path != '.': + logger.info(f'Creating report directory {arguments.report_img_path}') + os.mkdir(arguments.report_img_path) + + # 4 snapshots each + Nsnap = 7 + NFrames = movie.shape[0] + # show only frames at least one Tmin + # away from the edge (-effects) + start_frame = int(Wkwargs['Tmin'] / Wkwargs['dt']) + + if (start_frame > NFrames // 2): + logger.warning("Smallest period already is larger than half the observation time!") + # set to 0 in this case + start_frame = 0 + + frame_increment = int((NFrames - 2 * start_frame) / Nsnap) + snapshot_frames = range(start_frame, NFrames - start_frame, frame_increment) + + for snapshot_frame in snapshot_frames: + output_report.produce_snapshots(movie, results, snapshot_frame, Wkwargs, img_path=arguments.report_img_path) + + output_report.produce_distr_plots(results, Wkwargs, img_path=arguments.report_img_path) + + output_report.create_html(snapshot_frames, arguments.html_fname) + +except FileExistsError as e: + logger.critical(f"Could not create html report directory: {repr(e)}") + +# --- save out result movies --- + +# None means output is filtered from galaxy settings +if arguments.phase_out is not None: + # save phase movie + io.imsave(arguments.phase_out, results['phase'], plugin="tifffile") + logger.info(f'Written phase to {arguments.phase_out}') +if arguments.period_out is not None: + # save period movie + io.imsave(arguments.period_out, results['period'], plugin="tifffile") + logger.info(f'Written period to {arguments.period_out}') +if arguments.power_out is not None: + # save power movie + io.imsave(arguments.power_out, results['power'], plugin="tifffile") + logger.info(f'Written power to {arguments.power_out}') +if arguments.amplitude_out is not None: + # save amplitude movie + io.imsave(arguments.amplitude_out, results['amplitude'], plugin="tifffile") + logger.info(f'Written amplitude to {arguments.amplitude_out}') + +# save out the probably pre-processed (scaled and blurred) input movie for +# direct comparison to results and coordinate mapping etc. +if arguments.preprocessed_out is not None: + io.imsave(arguments.preprocessed_out, movie.astype(float32), plugin='tifffile') + logger.info(f'Written preprocessed to {arguments.preprocessed_out}') diff -r 000000000000 -r 76733d05d8ef test-data/output1.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test-data/output1.html Sat Nov 28 13:45:34 2020 +0000 @@ -0,0 +1,334 @@ + + + + SpyBOAT Output Report + + + + + + +

SpyBOAT Results Report

+
+

Distribution Dynamics

+ + +

Output Movie Snapshots

+ + +
+ + +
+ + + +
+

+ Frame Nr. 20

+ + +
+ +
+

+ Frame Nr. 28

+ + +
+ +
+

+ Frame Nr. 36

+ + +
+ +
+

+ Frame Nr. 44

+ + +
+ +
+

+ Frame Nr. 52

+ + +
+ +
+

+ Frame Nr. 60

+ + +
+ +
+

+ Frame Nr. 68

+ + +
+ + + + + + + + \ No newline at end of file diff -r 000000000000 -r 76733d05d8ef test-data/test-movie.tif Binary file test-data/test-movie.tif has changed