Skip to content
GitLab
Explore
Sign in
Primary navigation
Search or go to…
Project
Z
zolfa-nd2reader
Manage
Activity
Members
Labels
Plan
Wiki
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Snippets
Build
Pipelines
Jobs
Pipeline schedules
Artifacts
Deploy
Releases
Package registry
Container registry
Model registry
Operate
Environments
Terraform modules
Analyze
Value stream analytics
Contributor analytics
CI/CD analytics
Repository analytics
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
Community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
Lorenzo ZOLFANELLI
zolfa-nd2reader
Commits
f331c8fb
Commit
f331c8fb
authored
4 years ago
by
Lorenzo ZOLFANELLI
Browse files
Options
Downloads
Patches
Plain Diff
Add functions to read only rectangular regions.
parent
728a425a
Branches
Branches containing commit
Tags
Tags containing commit
No related merge requests found
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
nd2reader/parser.py
+106
-0
106 additions, 0 deletions
nd2reader/parser.py
nd2reader/reader.py
+15
-0
15 additions, 0 deletions
nd2reader/reader.py
with
121 additions
and
0 deletions
nd2reader/parser.py
+
106
−
0
View file @
f331c8fb
...
@@ -6,6 +6,7 @@ import six
...
@@ -6,6 +6,7 @@ import six
import
warnings
import
warnings
from
pims.base_frames
import
Frame
from
pims.base_frames
import
Frame
import
numpy
as
np
import
numpy
as
np
from
tqdm
import
tqdm
from
nd2reader.common
import
get_version
,
read_chunk
from
nd2reader.common
import
get_version
,
read_chunk
from
nd2reader.label_map
import
LabelMap
from
nd2reader.label_map
import
LabelMap
...
@@ -78,6 +79,38 @@ class Parser(object):
...
@@ -78,6 +79,38 @@ class Parser(object):
else
:
else
:
return
Frame
(
image
,
frame_no
=
frame_number
,
metadata
=
self
.
_get_frame_metadata
())
return
Frame
(
image
,
frame_no
=
frame_number
,
metadata
=
self
.
_get_frame_metadata
())
def
get_slice_by_attributes
(
self
,
xywh
,
frame_number
,
field_of_view
,
channel
,
z_level
,
height
,
width
):
"""
Gets a rectangular slice of an image based on its attributes alone
Args:
xywh: tuples containing (x, y, w, h) values of the
rectangular region to load
frame_number: the frame number
field_of_view: the field of view
channel_name: the color channel name
z_level: the z level
height: the height of the image
width: the width of the image
Returns:
Frame: the requested image
"""
frame_number
=
0
if
frame_number
is
None
else
frame_number
field_of_view
=
0
if
field_of_view
is
None
else
field_of_view
channel
=
0
if
channel
is
None
else
channel
z_level
=
0
if
z_level
is
None
else
z_level
image_group_number
=
self
.
_calculate_image_group_number
(
frame_number
,
field_of_view
,
z_level
)
try
:
timestamp
,
raw_image_data
=
self
.
_get_raw_slice_data
(
xywh
,
image_group_number
,
channel
,
height
,
width
)
except
(
TypeError
):
return
Frame
([],
frame_no
=
frame_number
,
metadata
=
self
.
_get_frame_metadata
())
else
:
return
Frame
(
raw_image_data
,
frame_no
=
frame_number
,
metadata
=
self
.
_get_frame_metadata
())
def
get_image_by_attributes
(
self
,
frame_number
,
field_of_view
,
channel
,
z_level
,
height
,
width
):
def
get_image_by_attributes
(
self
,
frame_number
,
field_of_view
,
channel
,
z_level
,
height
,
width
):
"""
Gets an image based on its attributes alone
"""
Gets an image based on its attributes alone
...
@@ -246,6 +279,79 @@ class Parser(object):
...
@@ -246,6 +279,79 @@ class Parser(object):
"""
"""
return
{
channel
:
n
for
n
,
channel
in
enumerate
(
self
.
metadata
[
"
channels
"
])}
return
{
channel
:
n
for
n
,
channel
in
enumerate
(
self
.
metadata
[
"
channels
"
])}
def
_get_raw_slice_data
(
self
,
xywh
,
image_group_number
,
channel
,
height
,
width
):
"""
Reads the raw bytes and the timestamp of a rectangular slice
of an image.
Args:
xywh: tuples containing (x, y, w, h) values of the
rectangular region to load
image_group_number: the image group number (see _calculate_image_group_number)
channel: the position (int) of the channel to load
height: the height of the image
width: the width of the image
Returns:
"""
size_c
=
len
(
self
.
metadata
[
"
channels
"
])
x0
,
y0
,
w
,
h
=
xywh
chunk_location
=
self
.
_label_map
.
get_image_data_location
(
image_group_number
)
fh
=
self
.
_fh
if
chunk_location
is
None
or
fh
is
None
:
return
None
fh
.
seek
(
chunk_location
)
# The chunk metadata is always 16 bytes long
chunk_metadata
=
fh
.
read
(
16
)
header
,
relative_offset
,
data_length
=
struct
.
unpack
(
"
IIQ
"
,
chunk_metadata
)
if
header
!=
0xabeceda
:
raise
ValueError
(
"
The ND2 file seems to be corrupted.
"
)
# We start at the location of the chunk metadata, skip over the metadata, and then proceed to the
# start of the actual data field, which is at some arbitrary place after the metadata.
fh
.
seek
(
chunk_location
+
16
+
relative_offset
)
# Read timestamp (8 bytes)
timestamp
=
struct
.
unpack
(
"
d
"
,
fh
.
read
(
8
))[
0
]
# Stitched Images: evaluate number of bytes to strip
# (with stitched images sometimes after each row we have a regular number of extra bytes)
n_unwanted_bytes
=
(
data_length
-
8
)
%
(
height
*
width
)
assert
0
==
n_unwanted_bytes
%
height
rowskip
=
n_unwanted_bytes
//
height
# Read ROI: row-by-row
image_start_pos
=
chunk_location
+
16
+
relative_offset
+
8
line_bytemask
=
np
.
zeros
(
size_c
,
dtype
=
np
.
bool
)
line_bytemask
[
channel
]
=
True
line_bytemask
=
np
.
tile
(
line_bytemask
.
repeat
(
2
),
w
)
def
get_line
(
y
):
fh
.
seek
(
image_start_pos
+
size_c
*
2
*
((
width
)
*
y
+
x0
)
+
y
*
rowskip
)
return
np
.
frombuffer
(
fh
.
read
(
size_c
*
2
*
w
),
np
.
byte
)[
line_bytemask
]
data
=
[
get_line
(
y
)
for
y
in
tqdm
(
range
(
y0
,
y0
+
h
))]
data
=
bytes
().
join
(
data
)
image_group_data
=
array
.
array
(
"
H
"
,
data
)
true_channels_no
=
int
(
len
(
image_group_data
)
/
(
h
*
w
))
image_data
=
np
.
reshape
(
image_group_data
,
(
h
,
w
,
true_channels_no
))
missing_channels
=
~
np
.
any
(
image_data
,
axis
=
(
0
,
1
))
image_data
[...,
missing_channels
]
=
np
.
full
(
(
h
,
w
,
missing_channels
.
sum
()),
np
.
nan
)
if
np
.
any
(
missing_channels
):
warnings
.
warn
(
"
ND2 file contains gap frames which are represented by
"
+
"
np.nan-filled arrays; to convert to zeros use e.g.
"
+
"
np.nan_to_num(array)
"
)
return
timestamp
,
image_data
[...,
0
]
def
_get_raw_image_data
(
self
,
image_group_number
,
channel_offset
,
height
,
width
):
def
_get_raw_image_data
(
self
,
image_group_number
,
channel_offset
,
height
,
width
):
"""
Reads the raw bytes and the timestamp of an image.
"""
Reads the raw bytes and the timestamp of an image.
...
...
This diff is collapsed.
Click to expand it.
nd2reader/reader.py
+
15
−
0
View file @
f331c8fb
...
@@ -69,6 +69,21 @@ class ND2Reader(FramesSequenceND):
...
@@ -69,6 +69,21 @@ class ND2Reader(FramesSequenceND):
except
KeyError
:
except
KeyError
:
return
0
return
0
def
get_roi
(
self
,
roi
,
c
=
0
,
t
=
0
,
z
=
0
,
x
=
0
,
y
=
0
,
v
=
0
):
height
=
self
.
metadata
[
'
height
'
]
width
=
self
.
metadata
[
'
width
'
]
ylim
=
roi
[
0
].
indices
(
height
)
xlim
=
roi
[
1
].
indices
(
width
)
y
=
ylim
[
0
]
x
=
xlim
[
0
]
w
=
xlim
[
1
]
-
xlim
[
0
]
h
=
ylim
[
1
]
-
ylim
[
0
]
return
self
.
_parser
.
get_slice_by_attributes
(
(
x
,
y
,
w
,
h
),
t
,
v
,
c
,
z
,
height
,
width
)
def
get_frame_2D
(
self
,
c
=
0
,
t
=
0
,
z
=
0
,
x
=
0
,
y
=
0
,
v
=
0
):
def
get_frame_2D
(
self
,
c
=
0
,
t
=
0
,
z
=
0
,
x
=
0
,
y
=
0
,
v
=
0
):
"""
Gets a given frame using the parser
"""
Gets a given frame using the parser
Args:
Args:
...
...
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment