Skip to content
101 changes: 70 additions & 31 deletions neo/io/tiffio.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,35 +15,53 @@ class TiffIO(BaseIO):
"""
Neo IO module for optical imaging data stored as a folder of TIFF images.

*Usage*:
>>> from neo import io
>>> import quantities as pq
>>> r = io.TiffIO("dir_tiff",spatial_scale=1.0*pq.mm, units='V',
... sampling_rate=1.0*pq.Hz)
>>> block = r.read_block()
read block
creating segment
returning block
>>> block
Block with 1 segments
file_origin: 'test'
# segments (N=1)
0: Segment with 1 imagesequences
annotations: {'tiff_file_names': ['file_tif_1_.tiff',
'file_tif_2.tiff',
'file_tif_3.tiff',
'file_tif_4.tiff',
'file_tif_5.tiff',
'file_tif_6.tiff',
'file_tif_7.tiff',
'file_tif_8.tiff',
'file_tif_9.tiff',
'file_tif_10.tiff',
'file_tif_11.tiff',
'file_tif_12.tiff',
'file_tif_13.tiff',
'file_tif_14.tiff']}
# analogsignals (N=0)
Parameters
----------
directory_path: Path | str | None, default: None
The path to the folder containing tiff images
units: Quantity units | None, default: None
the units for creating the ImageSequence
sampling_rate: Quantity Units | None, default: None
The sampling rate
spatial_scale: Quantity unit | None, default: None
The scale of the images
origin: Literal['top-left'| 'bottom-left'], default: 'top-left'
Whether to use the python default origin for images which is upper left corner ('top-left')
as orgin or to use a bottom left corner as orgin ('bottom-left')
Note that plotting functions like matplotlib.pyplot.imshow expect upper left corner.
**kwargs: dict
The standard neo annotation kwargs

Examples
--------
>>> from neo import io
>>> import quantities as pq
>>> r = io.TiffIO("dir_tiff",spatial_scale=1.0*pq.mm, units='V',
... sampling_rate=1.0*pq.Hz)
>>> block = r.read_block()
read block
creating segment
returning block
>>> block
Block with 1 segments
file_origin: 'test'
# segments (N=1)
0: Segment with 1 imagesequences
annotations: {'tiff_file_names': ['file_tif_1_.tiff',
'file_tif_2.tiff',
'file_tif_3.tiff',
'file_tif_4.tiff',
'file_tif_5.tiff',
'file_tif_6.tiff',
'file_tif_7.tiff',
'file_tif_8.tiff',
'file_tif_9.tiff',
'file_tif_10.tiff',
'file_tif_11.tiff',
'file_tif_12.tiff',
'file_tif_13.tiff',
'file_tif_14.tiff']}
# analogsignals (N=0)
"""

name = "TIFF IO"
Expand All @@ -66,13 +84,30 @@ class TiffIO(BaseIO):

mode = "dir"

def __init__(self, directory_path=None, units=None, sampling_rate=None, spatial_scale=None, **kwargs):
import PIL
def __init__(
self,
directory_path=None,
units=None,
sampling_rate=None,
spatial_scale=None,
origin='top-left',
**kwargs,
):
# this block is because people might be confused about the PIL -> pillow change
# between python2 -> python3 (both with namespace PIL)
try:
import PIL
except ImportError:
raise ImportError("To use TiffIO you must first `pip install pillow`")

if origin != 'top-left' and origin != 'bottom-left':
raise ValueError("`origin` must be either `top-left` or `bottom-left`")

BaseIO.__init__(self, directory_path, **kwargs)
self.units = units
self.sampling_rate = sampling_rate
self.spatial_scale = spatial_scale
self.origin = origin

def read_block(self, lazy=False, **kwargs):
import PIL
Expand All @@ -98,13 +133,17 @@ def natural_sort(l):
list_data_image = []
for file_name in file_name_list:
data = np.array(PIL.Image.open(self.filename + "/" + file_name)).astype(np.float32)
if self.origin == "bottom-left":
data = np.flip(data, axis=-2)
list_data_image.append(data)
list_data_image = np.array(list_data_image)
if len(list_data_image.shape) == 4:
list_data_image = []
for file_name in file_name_list:
image = PIL.Image.open(self.filename + "/" + file_name).convert("L")
data = np.array(image).astype(np.float32)
if self.origin == "bottom-left":
data = np.flip(data, axis=-2)
list_data_image.append(data)

print("read block")
Expand Down
24 changes: 20 additions & 4 deletions neo/test/iotest/test_tiffio.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,14 @@ def test_read_group_of_tiff_grayscale(self):
img = []
for picture in range(10):
img.append([])
for y in range(50):
for x in range(50):
img[picture].append([])
for x in range(50):
img[picture][y].append(x)
for y in range(50):
img[picture][x].append(y)
img = np.array(img, dtype=float)
for image in range(10):
Image.fromarray(img[image]).save(directory + "/tiff_exemple" + str(image) + ".tif")
# rotate image by 90 deg so that shifting the origin is meaningful in later test
Image.fromarray(np.rot90(img[image])).save(directory + "/tiff_exemple" + str(image) + ".tif")

ioclass = TiffIO(
directory_path=directory, units="V", sampling_rate=1.0 * pq.Hz, spatial_scale=1.0 * pq.micrometer
Expand All @@ -35,6 +36,21 @@ def test_read_group_of_tiff_grayscale(self):
self.assertEqual(blck.segments[0].imagesequences[0].sampling_rate, 1.0 * pq.Hz)
self.assertEqual(blck.segments[0].imagesequences[0].spatial_scale, 1.0 * pq.micrometer)

ioclass_bl_origin = TiffIO(
directory_path=directory,
units="V",
sampling_rate=1.0 * pq.Hz,
spatial_scale=1.0 * pq.micrometer,
origin='bottom-left',
)
blck_bl_origin = ioclass_bl_origin.read_block()

self.assertAlmostEqual(
blck.segments[0].imagesequences[0][0][0,0].magnitude,
blck_bl_origin.segments[0].imagesequences[0][0][49,0].magnitude, # since flipped over y, [0,0] == [49,0]
places=3,
)

# end of directory
shutil.rmtree(directory)

Expand Down
Loading