diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..5c9a041 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,12 @@ +FROM python:3.10 + +WORKDIR /app + +COPY pyproject.toml poetry.lock ./ + +RUN pip install poetry +RUN poetry install --no-dev + +COPY . . + +CMD ["python", "-m", "unittest"] diff --git a/picasso_workflow/analyse.py b/picasso_workflow/analyse.py index 61b9c3f..8d9a4fa 100644 --- a/picasso_workflow/analyse.py +++ b/picasso_workflow/analyse.py @@ -657,6 +657,52 @@ def _plot_locs_vs_frame(self, filename): plt.close(fig) return results + @module_decorator + def export_brightfield(self, i, parameters, results): + """Opens a single-plane tiff image and saves it to png with + contrast adjustment. + + Args: + i : int + the module index in the protocol + parameters : dict + necessary items: + filepath : str or list of str + the tiff file(s) to load. The converted file(s) will + have the same name, but with .png extension + optional items: + min_quantile : float, default: 0 + the quantile below which pixels are shown black + max_quantile : float, default: 1 + the quantile above which pixels are shown white + results : dict + the results dict, created by the module_decorator + Returns: + parameters : dict + as input, potentially changed values, for consistency + results : dict + the analysis results + filename : the png file + """ + fps_in = parameters["filepath"] + if isinstance(fps_in, str): + fps_in = [fps_in] + fps_out = [] + for fp in fps_in: + mov, _ = io.load_movie(fp) + frame = mov[0] + fn = os.path.split(fp)[1] + fn = os.path.splitext(fn)[0] + ".png" + fp = os.path.join(results["folder"], fn) + fps_out.append(fp) + min_quantile = parameters.get("min_quantile", 0) + max_quantile = parameters.get("max_quantile", 1) + process_brightfield.save_frame( + fp, frame, min_quantile, max_quantile + ) + results["filenames"] = fps_out + return parameters, results + @module_decorator def undrift_rcc(self, i, parameters, results): """Undrifts localized data using redundant cross correlation. diff --git a/picasso_workflow/confluence.py b/picasso_workflow/confluence.py index 3466746..8f88a88 100644 --- a/picasso_workflow/confluence.py +++ b/picasso_workflow/confluence.py @@ -227,6 +227,34 @@ def localize(self, i, parameters, results): os.path.split(res["filename"])[1], ) + def export_brightfield(self, i, parameters, results): + """Describes the export_brightfield section of picasso + Args: + """ + logger.debug("Reporting export_brightfield.") + text = """ + +

Exporting Brightfield

+
    + """ + text += f""" +
  • Start Time: {results['start time']}
  • +
  • Duration: {results["duration"] // 60:.0f} min + {(results["duration"] % 60):.02f} s
+
+ """ + self.ci.update_page_content( + self.report_page_name, self.report_page_id, text + ) + + for fp in results.get("filepaths"): + self.ci.upload_attachment(self.report_page_id, fp) + self.ci.update_page_content_with_image_attachment( + self.report_page_name, + self.report_page_id, + os.path.split(fp)[1], + ) + def undrift_rcc(self, i, parameters, results): """Describes the Localize section of picasso Args: diff --git a/picasso_workflow/process_brightfield.py b/picasso_workflow/process_brightfield.py index 68bb2e8..508bdae 100644 --- a/picasso_workflow/process_brightfield.py +++ b/picasso_workflow/process_brightfield.py @@ -8,6 +8,7 @@ """ import logging from moviepy.editor import ImageSequenceClip +from imageio import imsave # package is dependency of moviepy import numpy as np logger = logging.getLogger(__name__) @@ -62,3 +63,23 @@ def save_movie(fname, movie, min_quantile=0, max_quantile=1, fps=1): # Create movie file clip = ImageSequenceClip(adjusted_images, fps=fps) clip.write_videofile(fname, verbose=False) # , codec='mpeg4') + + +def save_frame(pathname, frame, min_quantile=0, max_quantile=1): + """Save a grayscale frame to png + Args: + pathname : str + the file name to save + frame : 2D np array (x,y) + the frame to save + min_quantile : float, default: 0 + the quantile below which pixels are shown black + max_quantile : float, default: 1 + the quantile above which pixels are shown white + """ + logger.debug(frame.shape) + adjusted_frame = adjust_contrast( + frame, min_quantile, max_quantile + ) # [..., np.newaxis] + logger.debug(adjusted_frame.shape) + imsave(pathname, adjusted_frame) diff --git a/picasso_workflow/tests/test_analyse.py b/picasso_workflow/tests/test_analyse.py index 4130845..761723d 100644 --- a/picasso_workflow/tests/test_analyse.py +++ b/picasso_workflow/tests/test_analyse.py @@ -178,6 +178,19 @@ def localize(self, mock_get_spots, mock_fit_spot, mock_locs_from_fits): shutil.rmtree(os.path.join(self.results_folder, "00_localize")) + @patch("picasso_workflow.analyse.io.load_movie") + def export_brightfield(self, mock_load): + frame = np.random.randint(0, 1000, size=(1, 32, 32)) + mock_load.return_value = (frame, []) + + parameters = {"filepath": "myfp.ome.tiff"} + + parameters, results = self.ap.export_brightfield(0, parameters) + + shutil.rmtree( + os.path.join(self.results_folder, "00_export_brightfield") + ) + @patch("picasso_workflow.analyse.postprocess.undrift") def undrift_rcc(self, mock_undrift_rcc): nspots = 5 diff --git a/picasso_workflow/tests/test_confluence.py b/picasso_workflow/tests/test_confluence.py index fe86a16..dcee92c 100644 --- a/picasso_workflow/tests/test_confluence.py +++ b/picasso_workflow/tests/test_confluence.py @@ -233,6 +233,22 @@ def localize(self): ) self.cr.ci.delete_page(pgid) + # @unittest.skip("") + def export_brightfield(self): + parameters = {} + results = { + "start time": "now", + "duration": 16.4, + "filepaths": ["myfp.png"], + } + self.cr.export_brightfield(0, parameters, results) + + # clean up + pgid, pgtitle = self.cr.ci.get_page_properties( + self.cr.report_page_name + ) + self.cr.ci.delete_page(pgid) + # @unittest.skip("") def undrift_rcc(self): parameters = { diff --git a/picasso_workflow/util.py b/picasso_workflow/util.py index 8b7293d..93b0c41 100644 --- a/picasso_workflow/util.py +++ b/picasso_workflow/util.py @@ -56,6 +56,12 @@ def localize(self): """Localizes Spots previously identified.""" pass + @abc.abstractmethod + def export_brightfield(self): + """Opens a single-plane tiff image and saves it to png with + contrast adjustment.""" + pass + @abc.abstractmethod def undrift_rcc(self): """Undrifts localized data using redundant cross correlation."""