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

RF: Migrate duplicated rois2masks code to function in roitools #187

Merged
merged 3 commits into from
Jun 26, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
134 changes: 40 additions & 94 deletions fissa/extraction.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,24 @@ def getmean(data):
raise NotImplementedError()

@staticmethod
def rois2masks(rois, data):
def get_frame_size(data):
"""
Determine the shape of each frame within the recording.

Parameters
----------
data : data_type
The same object as returned by :meth:`image2array`.

Returns
-------
shape : tuple of ints
The 2D, y-by-x, shape of each frame in the movie.
"""
raise NotImplementedError()

@classmethod
def rois2masks(cls, rois, data):
"""
Convert ROIs into a collection of binary masks.

Expand All @@ -97,7 +114,7 @@ def rois2masks(rois, data):
--------
fissa.roitools.getmasks, fissa.roitools.readrois
"""
raise NotImplementedError()
return roitools.rois2masks(rois, cls.get_frame_size(data))

@staticmethod
def extracttraces(data, masks):
Expand Down Expand Up @@ -203,45 +220,21 @@ def getmean(data):
return data.mean(axis=0, dtype=np.float64)

@staticmethod
def rois2masks(rois, data):
"""Take the object `rois` and returns it as a list of binary masks.
def get_frame_size(data):
"""
Determine the shape of each frame within the recording.

Parameters
----------
rois : str or :term:`list` of :term:`array_like`
Either a string containing a path to an ImageJ roi zip file,
or a list of arrays encoding polygons, or list of binary arrays
representing masks.
data : :term:`array_like`
Data array as made by :meth:`image2array`. Must be shaped
``(frames, y, x)``.
data : data_type
The same object as returned by :meth:`image2array`.

Returns
-------
masks : :term:`list` of :class:`numpy.ndarray`
List of binary arrays.
shape : tuple of ints
The 2D, y-by-x, shape of each frame in the movie.
"""
# get the image shape
shape = data.shape[1:]

# if it's a list of strings
if isinstance(rois, basestring):
rois = roitools.readrois(rois)

if not isinstance(rois, abc.Sequence):
raise TypeError(
'Wrong ROIs input format: expected a list or sequence, but got'
' a {}'.format(rois.__class__)
)

# if it's a something by 2 array (or vice versa), assume polygons
if np.shape(rois[0])[1] == 2 or np.shape(rois[0])[0] == 2:
return roitools.getmasks(rois, shape)
# if it's a list of bigger arrays, assume masks
elif np.shape(rois[0]) == shape:
return rois

raise ValueError('Wrong ROIs input format: unfamiliar shape.')
return data.shape[-2:]

@staticmethod
def extracttraces(data, masks):
Expand Down Expand Up @@ -356,44 +349,21 @@ def getmean(data):
return memory / n_frames

@staticmethod
def rois2masks(rois, data):
"""Take the object `rois` and returns it as a list of binary masks.
def get_frame_size(data):
"""
Determine the shape of each frame within the recording.

Parameters
----------
rois : str or list of array_like
Either a string containing a path to an ImageJ roi zip file,
or a list of arrays encoding polygons, or list of binary arrays
representing masks.
data : tifffile.TiffFile
Open tifffile.TiffFile object.
data : data_type
The same object as returned by :meth:`image2array`.

Returns
-------
masks : list of numpy.ndarray
List of binary arrays.
shape : tuple of ints
The 2D, y-by-x, shape of each frame in the movie.
"""
# Get the image shape
shape = data.pages[0].shape[-2:]

# If it's a string, parse the string
if isinstance(rois, basestring):
rois = roitools.readrois(rois)

if not isinstance(rois, abc.Sequence):
raise TypeError(
"Wrong ROIs input format: expected a list or sequence, but got"
" a {}".format(rois.__class__)
)

# If it's a something by 2 array (or vice versa), assume polygons
if np.shape(rois[0])[1] == 2 or np.shape(rois[0])[0] == 2:
return roitools.getmasks(rois, shape)
# If it's a list of bigger arrays, assume masks
elif np.shape(rois[0]) == shape:
return rois

raise ValueError("Wrong ROIs input format: unfamiliar shape.")
return data.pages[0].shape[-2:]

@staticmethod
def extracttraces(data, masks):
Expand Down Expand Up @@ -497,45 +467,21 @@ def getmean(data):
return avg

@staticmethod
def rois2masks(rois, data):
def get_frame_size(data):
"""
Take the object `rois` and returns it as a list of binary masks.
Determine the shape of each frame within the recording.

Parameters
----------
rois : str or :term:`list` of :term:`array_like`
Either a string containing a path to an ImageJ roi zip file,
or a list of arrays encoding polygons, or list of binary arrays
representing masks.
data : PIL.Image
An open :class:`PIL.Image` handle to a multi-frame TIFF image.

Returns
-------
masks : list of numpy.ndarray
List of binary arrays.
shape : tuple of ints
The 2D, y-by-x, shape of each frame in the movie.
"""
# get the image shape
shape = data.size[::-1]

# If rois is string, we first need to read the contents of the file
if isinstance(rois, basestring):
rois = roitools.readrois(rois)

if not isinstance(rois, abc.Sequence):
raise TypeError(
'Wrong ROIs input format: expected a list or sequence, but got'
' a {}'.format(rois.__class__)
)

# if it's a something by 2 array (or vice versa), assume polygons
if np.shape(rois[0])[1] == 2 or np.shape(rois[0])[0] == 2:
return roitools.getmasks(rois, shape)
# if it's a list of bigger arrays, assume masks
elif np.shape(rois[0]) == shape:
return rois

raise ValueError('Wrong ROIs input format: unfamiliar shape.')
return data.size[::-1]

@staticmethod
def extracttraces(data, masks):
Expand Down
44 changes: 44 additions & 0 deletions fissa/roitools.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,14 @@
from __future__ import division

import numpy as np
from past.builtins import basestring
from skimage.measure import find_contours

try:
from collections import abc
except ImportError:
import collections as abc

from .readimagejrois import read_imagej_roi_zip
from .ROI import poly2mask

Expand Down Expand Up @@ -434,3 +440,41 @@ def find_roi_edge(mask):
outline[i] -= 0.5

return outline


def rois2masks(rois, shape):
"""
Convert ROIs into a list of binary masks.

Parameters
----------
rois : str or list of array_like
Either a string containing a path to an ImageJ roi zip file,
or a list of arrays encoding polygons, or list of binary arrays
representing masks.
shape : array_like
Image shape as a length 2 vector.

Returns
-------
masks : list of numpy.ndarray
List of binary arrays.
"""
# If it's a string, parse the string
if isinstance(rois, basestring):
rois = readrois(rois)

if not isinstance(rois, abc.Sequence):
raise TypeError(
"Wrong ROIs input format: expected a list or sequence, but got"
" a {}".format(rois.__class__)
)

# If it's a something by 2 array (or vice versa), assume polygons
if np.shape(rois[0])[1] == 2 or np.shape(rois[0])[0] == 2:
return getmasks(rois, shape)
# If it's a list of bigger arrays, assume masks
elif np.shape(rois[0]) == shape:
return rois

raise ValueError("Wrong ROIs input format: unfamiliar shape.")
9 changes: 9 additions & 0 deletions fissa/tests/test_extraction.py
Original file line number Diff line number Diff line change
Expand Up @@ -486,6 +486,15 @@ def test_polys_3d(self):
self.datahandler.rois2masks(polys3d, self.data)


class TestRois2MasksRoitools(BaseTestCase, Rois2MasksTestMixin):
"""Test roitools.rois2masks."""

def setUp(self):
Rois2MasksTestMixin.setUp(self)
self.data = (176, 156)
self.datahandler = roitools


class TestRois2MasksTifffile(BaseTestCase, Rois2MasksTestMixin):
"""Tests for rois2masks using `~extraction.DataHandlerTifffile`."""

Expand Down