Skip to content

Commit

Permalink
Merge pull request #643 from mrariden/export_rois
Browse files Browse the repository at this point in the history
Export ROIs directly to ImageJ
  • Loading branch information
carsen-stringer committed May 19, 2023
2 parents aa16d39 + c8fdf9a commit 26263c6
Show file tree
Hide file tree
Showing 12 changed files with 106 additions and 8 deletions.
4 changes: 3 additions & 1 deletion cellpose/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@

import logging


# settings re-grouped a bit
def main():
""" Run cellpose from command line
"""
Expand Down Expand Up @@ -178,6 +178,8 @@ def main():
save_flows=args.save_flows,save_outlines=args.save_outlines,
save_ncolor=args.save_ncolor,dir_above=args.dir_above,savedir=args.savedir,
save_txt=args.save_txt,in_folders=args.in_folders)
if args.save_rois:
io.save_rois(masks, image_name)
logger.info('>>>> completed in %0.3f sec'%(time.time()-tic))
else:

Expand Down
2 changes: 2 additions & 0 deletions cellpose/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,8 @@ def get_arg_parser():
help='whether or not to save RGB images of flows when masks are saved (disabled by default)')
output_args.add_argument('--save_outlines', action='store_true',
help='whether or not to save RGB outline images when masks are saved (disabled by default)')
output_args.add_argument('--save_rois', action='store_true',
help='whether or not to save ImageJ compatible ROI archive (disabled by default)')
output_args.add_argument('--save_ncolor', action='store_true',
help='whether or not to save minimal "n-color" masks (disabled by default')
output_args.add_argument('--save_txt', action='store_true',
Expand Down
1 change: 1 addition & 0 deletions cellpose/gui/gui.py
Original file line number Diff line number Diff line change
Expand Up @@ -1835,6 +1835,7 @@ def enable_buttons(self):
self.saveFlows.setEnabled(True)
self.saveServer.setEnabled(True)
self.saveOutlines.setEnabled(True)
self.saveROIs.setEnabled(True)
self.toggle_mask_ops()

self.update_plot()
Expand Down
22 changes: 20 additions & 2 deletions cellpose/gui/guiparts.py
Original file line number Diff line number Diff line change
Expand Up @@ -279,8 +279,14 @@ def __init__(self, parent=None):
<li class="has-line-data" data-line-start="12" data-line-end="13">Start draw mask = right-click</li>
<li class="has-line-data" data-line-start="13" data-line-end="15">End draw mask = right-click, or return to circle at beginning</li>
</ul>
<p class="has-line-data" data-line-start="15" data-line-end="16">Overlaps in masks are NOT allowed. If you draw a mask on top of another mask, it is cropped so that it doesn’t overlap with the old mask. Masks in 2D should be single strokes (single stroke is checked). If you want to draw masks in 3D (experimental), then you can turn this option off and draw a stroke on each plane with the cell and then press ENTER. 3D labelling will fill in planes that you have not labelled so that you do not have to as densely label.</p>
<p class="has-line-data" data-line-start="17" data-line-end="18">!NOTE!: The GUI automatically saves after you draw a mask in 2D but NOT after 3D mask drawing and NOT after segmentation. Save in the file menu or with Ctrl+S. The output file is in the same folder as the loaded image with <code>_seg.npy</code> appended.</p>
<p class="has-line-data" data-line-start="15" data-line-end="16">Overlaps in masks are NOT allowed. If you \
draw a mask on top of another mask, it is cropped so that it doesn’t overlap with the old mask. Masks in 2D \
should be single strokes (single stroke is checked). If you want to draw masks in 3D (experimental), then \
you can turn this option off and draw a stroke on each plane with the cell and then press ENTER. 3D \
labelling will fill in planes that you have not labelled so that you do not have to as densely label.</p>
<p class="has-line-data" data-line-start="17" data-line-end="18">!NOTE!: The GUI automatically saves after \
you draw a mask in 2D but NOT after 3D mask drawing and NOT after segmentation. Save in the file menu or \
with Ctrl+S. The output file is in the same folder as the loaded image with <code>_seg.npy</code> appended.</p>
<table class="table table-striped table-bordered">
<br><br>
FYI there are tooltips throughout the GUI (hover over text to see)
Expand Down Expand Up @@ -329,6 +335,18 @@ def __init__(self, parent=None):
<td>load masks file (must be same size as image with 0 for NO mask, and 1,2,3… for masks)</td>
</tr>
<tr>
<td>CTRL+N</td>
<td>save masks as PNG</td>
</tr>
<tr>
<td>CTRL+R</td>
<td>save ROIs to native ImageJ ROI format</td>
</tr>
<tr>
<td>CTRL+F</td>
<td>save flows to image file</td>
</tr>
<tr>
<td>A/D or LEFT/RIGHT</td>
<td>cycle through images in current directory</td>
</tr>
Expand Down
12 changes: 10 additions & 2 deletions cellpose/gui/io.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import fastremap

from .. import utils, plot, transforms, models
from ..io import imread, imsave, outlines_to_text, add_model, remove_model
from ..io import imread, imsave, outlines_to_text, add_model, remove_model, save_rois
from ..transforms import normalize99

try:
Expand Down Expand Up @@ -452,7 +452,15 @@ def _save_flows(parent):
if len(parent.flows) > 0:
imsave(base + '_cp_flows.tif', parent.flows[4][:-1])
imsave(base + '_cp_cellprob.tif', parent.flows[4][-1])


def _save_rois(parent):
""" save masks as rois in .zip file for ImageJ """
filename = parent.filename
if parent.NZ == 1:
print(f'GUI_INFO: saving {parent.cellpix[0].max()} ImageJ ROIs to .zip archive.')
save_rois(parent.cellpix[0], parent.filename)
else:
print('ERROR: cannot save 3D outlines')

def _save_outlines(parent):
filename = parent.filename
Expand Down
6 changes: 6 additions & 0 deletions cellpose/gui/menus.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,12 @@ def mainmenu(parent):
file_menu.addAction(parent.saveOutlines)
parent.saveOutlines.setEnabled(False)

parent.saveROIs = QAction("Save outlines as .zip archive of &ROI files for ImageJ", parent)
parent.saveROIs.setShortcut("Ctrl+R")
parent.saveROIs.triggered.connect(lambda: io._save_rois(parent))
file_menu.addAction(parent.saveROIs)
parent.saveROIs.setEnabled(False)

parent.saveFlows = QAction("Save &Flows and cellprob as tif", parent)
parent.saveFlows.setShortcut("Ctrl+F")
parent.saveFlows.triggered.connect(lambda: io._save_flows(parent))
Expand Down
29 changes: 29 additions & 0 deletions cellpose/io.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from tqdm import tqdm
from pathlib import Path
from . import version_str
from roifile import ImagejRoi, roiwrite


try:
Expand Down Expand Up @@ -379,6 +380,34 @@ def save_to_png(images, masks, flows, file_names):
"""
save_masks(images, masks, flows, file_names, png=True)


def save_rois(masks, file_name):
""" save masks to .roi files in .zip archive for ImageJ/Fiji
Parameters
----------
masks: 2D array, int
masks output from Cellpose.eval, where 0=NO masks; 1,2,...=mask labels
file_name: str
name to save the .zip file to
-------
"""
outlines = utils.outlines_list(masks)
rois = [ImagejRoi.frompoints(outline) for outline in outlines]
file_name = os.path.splitext(file_name)[0] + '_rois.zip'

# Delete file if it exists; the roifile lib appends to existing zip files.
# If the user removed a mask it will still be in the zip file
if os.path.exists(file_name):
os.remove(file_name)

roiwrite(file_name, rois)


# Now saves flows, masks, etc. to separate folders.
def save_masks(images, masks, flows, file_names, png=True, tif=False, channels=[0,0],
suffix='',save_flows=False, save_outlines=False, save_ncolor=False,
Expand Down
6 changes: 6 additions & 0 deletions docs/gui.rst
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,12 @@ Keyboard shortcuts
| CTRL+M | load masks file (must be same size as image |
| | with 0 for NO mask, and 1,2,3... for masks) |
+---------------------+-----------------------------------------------+
| CTRL+N | save masks as PNG |
+---------------------+-----------------------------------------------+
| CTRL+R | save ROIs to native ImageJ ROI format |
+---------------------+-----------------------------------------------+
| CTRL+F | save flows to image file |
+---------------------+-----------------------------------------------+
| A/D or LEFT/RIGHT | cycle through images in current directory |
+---------------------+-----------------------------------------------+
| W/S or UP/DOWN | change color (RGB/gray/red/green/blue) |
Expand Down
2 changes: 1 addition & 1 deletion docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ Also check out these resources:
Cellpose 2.0

- `paper <https://www.biorxiv.org/content/10.1101/2022.04.01.486764v1>`_ on biorxiv
- `talk <https://www.youtube.com/watch?v=3ydtAhfq6H0`_
- `talk <https://www.youtube.com/watch?v=3ydtAhfq6H0>`_
- twitter `thread <https://twitter.com/marius10p/status/1511415409047650307?s=20&t=umTVIG1CFKIWHYMrQqFKyQ>`_
- human-in-the-loop training protocol `video <https://youtu.be/3Y1VKcxjNy4>`_

Expand Down
26 changes: 25 additions & 1 deletion docs/outputs.rst
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,31 @@ Or use the function below if running in a notebook
from cellpose import io
io.save_to_png(images, masks, flows, image_names)

ROI manager compatible output for ImageJ
Native ImageJ ROI archive output
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
You can save the outlines of the ROIs in a ImageJ-native ROI archive file. Rather than using the legacy solution below,
you can use this function to create an ROI archive file that can be opened in directly in ImageJ. Recent versions of
ImageJ can autodetect the file format. Open in ImageJ using File > Open... and select the file.
The ROIs will appear in the ROI manager.

To save the outlines using the CLI use the flag ``--save_rois``.

To save the outlines using the API use the ``save_rois`` function in ``io.py``:

This function is also available in the GUI.

::

from cellpose import io, utils

# image_name is file name of image
# masks is numpy array of masks for image
io.save_rois(masks, '<your_filename_string>')

# the file will be saved as '<your_filename_string>_rois.zip'


(Legacy ImageJ Interface) ROI manager compatible output for ImageJ
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

You can save the outlines of ROIs in a text file that's compatible with ImageJ
Expand Down
1 change: 1 addition & 0 deletions environment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,6 @@ dependencies:
- tifffile
- fastremap
- cellpose
- roifile


3 changes: 2 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
'torch>=1.6',
'opencv-python-headless',
'fastremap',
'imagecodecs'
'imagecodecs',
'roifile',
]

gui_deps = [
Expand Down

0 comments on commit 26263c6

Please sign in to comment.