Skip to content

Commit 17f31d2

Browse files
authored
Match with pylibjpeg encoding interface (#6)
1 parent 778c5a1 commit 17f31d2

File tree

3 files changed

+69
-55
lines changed

3 files changed

+69
-55
lines changed

rle/__init__.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
"""Set package shortcuts."""
22

33
from rle._version import __version__
4-
from rle.utils import pixel_array, generate_frames, decode_pixel_data
4+
from rle.utils import (
5+
pixel_array, generate_frames, decode_pixel_data, encode_pixel_data
6+
)

rle/tests/test_utils.py

+30-18
Original file line numberDiff line numberDiff line change
@@ -59,9 +59,9 @@ def test_u8_1s_1f_by_kwargs(self):
5959
kwargs = {}
6060
kwargs['rows'] = ds.Rows
6161
kwargs['columns'] = ds.Columns
62-
kwargs['samples_per_px'] = ds.SamplesPerPixel
63-
kwargs['bits_per_px'] = ds.BitsAllocated
64-
kwargs['nr_frames'] = int(getattr(ds, "NumberOfFrames", 1))
62+
kwargs['samples_per_pixel'] = ds.SamplesPerPixel
63+
kwargs['bits_allocated'] = ds.BitsAllocated
64+
kwargs['number_of_frames'] = int(getattr(ds, "NumberOfFrames", 1))
6565

6666
ref = ds.pixel_array
6767
gen = encode_array(ref, **kwargs)
@@ -105,9 +105,9 @@ def test_u32_3s_2f_by_kwargs(self):
105105
kwargs = {}
106106
kwargs['rows'] = ds.Rows
107107
kwargs['columns'] = ds.Columns
108-
kwargs['samples_per_px'] = ds.SamplesPerPixel
109-
kwargs['bits_per_px'] = ds.BitsAllocated
110-
kwargs['nr_frames'] = int(getattr(ds, "NumberOfFrames", 1))
108+
kwargs['samples_per_pixel'] = ds.SamplesPerPixel
109+
kwargs['bits_allocated'] = ds.BitsAllocated
110+
kwargs['number_of_frames'] = int(getattr(ds, "NumberOfFrames", 1))
111111

112112
ref = ds.pixel_array
113113
gen = encode_array(ref, **kwargs)
@@ -137,8 +137,8 @@ def test_bad_byteorder_raises(self):
137137
kwargs = {
138138
'rows': 0,
139139
'columns': 0,
140-
'samples_per_px': 1,
141-
'bits_per_px': 16,
140+
'samples_per_pixel': 1,
141+
'bits_allocated': 16,
142142
'byteorder': '=',
143143
}
144144

@@ -153,17 +153,29 @@ def test_bad_byteorder_raises(self):
153153
with pytest.raises(ValueError, match=msg):
154154
encode_pixel_data(b'', **kwargs)
155155

156+
def test_no_byteorder_u8(self):
157+
"""Test exception raised if invalid byteorder."""
158+
kwargs = {
159+
'rows': 1,
160+
'columns': 1,
161+
'samples_per_pixel': 1,
162+
'bits_allocated': 8,
163+
'byteorder': None,
164+
}
165+
166+
assert b'\x00\x01' == encode_pixel_data(b'\x01', **kwargs)[64:]
167+
156168
def test_bad_samples_raises(self):
157169
"""Test exception raised if invalid samples per pixel."""
158170
kwargs = {
159171
'rows': 0,
160172
'columns': 0,
161-
'samples_per_px': 0,
162-
'bits_per_px': 0,
173+
'samples_per_pixel': 0,
174+
'bits_allocated': 0,
163175
'byteorder': '<',
164176
}
165177

166-
msg = r"'samples_per_px' must be 1 or 3"
178+
msg = r"'samples_per_pixel' must be 1 or 3"
167179
with pytest.raises(ValueError, match=msg):
168180
encode_pixel_data(b'', **kwargs)
169181

@@ -172,12 +184,12 @@ def test_bad_bits_allocated_raises(self):
172184
kwargs = {
173185
'rows': 0,
174186
'columns': 0,
175-
'samples_per_px': 1,
176-
'bits_per_px': 2,
187+
'samples_per_pixel': 1,
188+
'bits_allocated': 2,
177189
'byteorder': '<',
178190
}
179191

180-
msg = r"'bits_per_px' must be 8, 16, 32 or 64"
192+
msg = r"'bits_allocated' must be 8, 16, 32 or 64"
181193
with pytest.raises(ValueError, match=msg):
182194
encode_pixel_data(b'', **kwargs)
183195

@@ -186,8 +198,8 @@ def test_bad_length_raises(self):
186198
kwargs = {
187199
'rows': 1,
188200
'columns': 1,
189-
'samples_per_px': 1,
190-
'bits_per_px': 8,
201+
'samples_per_pixel': 1,
202+
'bits_allocated': 8,
191203
'byteorder': '<',
192204
}
193205

@@ -200,8 +212,8 @@ def test_too_many_segments_raises(self):
200212
kwargs = {
201213
'rows': 1,
202214
'columns': 1,
203-
'samples_per_px': 3,
204-
'bits_per_px': 64,
215+
'samples_per_pixel': 3,
216+
'bits_allocated': 64,
205217
'byteorder': '<',
206218
}
207219

rle/utils.py

+36-36
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ def decode_pixel_data(src: bytes, ds: "Dataset", **kwargs) -> "np.ndarray":
2020
ds : pydicom.dataset.Dataset
2121
A :class:`~pydicom.dataset.Dataset` containing the group ``0x0028``
2222
elements corresponding to the image frame.
23-
kwargs : dict, optional
24-
A dictionary containing options for the decoder. Current options are:
23+
**kwargs
24+
Current decoding options are:
2525
2626
* ``{'byteorder': str}`` specify the byte ordering for the decoded data
2727
when more than 8 bits per pixel are used, should be '<' for little
@@ -64,17 +64,16 @@ def encode_array(
6464
The dataset corresponding to `arr` with matching values for *Rows*,
6565
*Columns*, *Samples per Pixel* and *Bits Allocated*. Required if
6666
the array properties aren't specified using `kwargs`.
67-
kwargs : dict, optional
68-
A dictionary containing keyword arguments. Required if `ds` isn't used,
69-
keys are:
67+
**kwargs
68+
Required keyword parameters if `ds` isn't used are:
7069
71-
* ``{'rows': int, 'columns': int}`` the number of rows and columns
72-
contained in `arr`.
73-
* ``{samples_per_px': int}`` the number of samples per pixel, either
70+
* ``'rows': int`` the number of rows contained in `src`
71+
* ``'columns': int`` the number of columns contained in `src`
72+
* ``samples_per_px': int`` the number of samples per pixel, either
7473
1 for monochrome or 3 for RGB or similar data.
75-
* ``{'bits_per_px': int}`` the number of bits needed to contain each
74+
* ``'bits_per_px': int`` the number of bits needed to contain each
7675
pixel, either 8, 16, 32 or 64.
77-
* ``{'nr_frames': int}`` the number of frames in `arr`, required if
76+
* ``'nr_frames': int`` the number of frames in `arr`, required if
7877
more than one frame is present.
7978
8079
Yields
@@ -91,11 +90,11 @@ def encode_array(
9190
if ds:
9291
kwargs['rows'] = ds.Rows
9392
kwargs['columns'] = ds.Columns
94-
kwargs['samples_per_px'] = ds.SamplesPerPixel
95-
kwargs['bits_per_px'] = ds.BitsAllocated
96-
kwargs['nr_frames'] = int(getattr(ds, "NumberOfFrames", 1))
93+
kwargs['samples_per_pixel'] = ds.SamplesPerPixel
94+
kwargs['bits_allocated'] = ds.BitsAllocated
95+
kwargs['number_of_frames'] = int(getattr(ds, "NumberOfFrames", 1) or 1)
9796

98-
if kwargs['nr_frames'] > 1:
97+
if kwargs['number_of_frames'] > 1:
9998
for frame in arr:
10099
yield encode_pixel_data(frame.tobytes(), **kwargs)
101100
else:
@@ -127,43 +126,42 @@ def encode_pixel_data(
127126
*Columns*, *Samples per Pixel* and *Bits Allocated*. Required if
128127
the frame properties aren't specified using `kwargs`.
129128
byteorder : str, optional
130-
Required if the samples per pixel is greater than 1 and the value is
131-
not passed using `kwargs`. If `src` is in little-endian byte order
132-
then ``'<'``, otherwise ``'>'`` for big-endian.
133-
kwargs : dict
134-
A dictionary containing keyword arguments. Required keys are:
135-
136-
* ``{'rows': int, 'columns': int}`` the number of rows and columns
137-
contained in `src`
138-
* ``{samples_per_px': int}`` the number of samples per pixel, either
129+
Required if the samples per pixel is greater than 1. If `src` is in
130+
little-endian byte order then ``'<'``, otherwise ``'>'`` for
131+
big-endian.
132+
**kwargs
133+
If `ds` is not used then the following are required:
134+
135+
* ``'rows': int`` the number of rows contained in `src`
136+
* ``'columns': int`` the number of columns contained in `src`
137+
* ``samples_per_pixel': int`` the number of samples per pixel, either
139138
1 for monochrome or 3 for RGB or similar data.
140-
* ``{'bits_per_px': int}`` the number of bits needed to contain each
139+
* ``'bits_allocated': int`` the number of bits needed to contain each
141140
pixel, either 8, 16, 32 or 64.
142-
* ``{'byteorder': str}``, required if the samples per pixel is greater
143-
than 1. If `src` is in little-endian byte order then ``'<'``,
144-
otherwise ``'>'`` for big-endian.
145141
146142
Returns
147143
-------
148144
bytes
149145
The RLE encoded frame.
150146
"""
151147
if ds:
152-
r, c = ds.Rows, ds.Columns
148+
r = ds.Rows
149+
c = ds.Columns
153150
bpp = ds.BitsAllocated
154151
spp = ds.SamplesPerPixel
155152
else:
156-
r, c = kwargs['rows'], kwargs['columns']
157-
bpp = kwargs['bits_per_px']
158-
spp = kwargs['samples_per_px']
153+
r = kwargs['rows']
154+
c = kwargs['columns']
155+
bpp = kwargs['bits_allocated']
156+
spp = kwargs['samples_per_pixel']
159157

160158
# Validate input
161159
if spp not in [1, 3]:
162-
src = "(0028,0002) 'Samples per Pixel'" if ds else "'samples_per_px'"
160+
src = "(0028,0002) 'Samples per Pixel'" if ds else "'samples_per_pixel'"
163161
raise ValueError(src + " must be 1 or 3")
164162

165163
if bpp not in [8, 16, 32, 64]:
166-
src = "(0028,0100) 'Bits Allocated'" if ds else "'bits_per_px'"
164+
src = "(0028,0100) 'Bits Allocated'" if ds else "'bits_allocated'"
167165
raise ValueError(src + " must be 8, 16, 32 or 64")
168166

169167
if bpp / 8 * spp > 15:
@@ -172,7 +170,8 @@ def encode_pixel_data(
172170
"Standard only allows a maximum of 15 segments"
173171
)
174172

175-
if bpp > 8 and byteorder not in ('<', '>'):
173+
byteorder = '<' if bpp == 8 else byteorder
174+
if byteorder not in ('<', '>'):
176175
raise ValueError(
177176
"A valid 'byteorder' is required when the number of bits per "
178177
"pixel is greater than 8"
@@ -236,8 +235,9 @@ def generate_frames(ds: "Dataset", reshape: bool = True) -> "np.ndarray":
236235
"elements are missing from the dataset: " + ", ".join(missing)
237236
)
238237

239-
nr_frames = getattr(ds, "NumberOfFrames", 1)
240-
r, c = ds.Rows, ds.Columns
238+
nr_frames = int(getattr(ds, "NumberOfFrames", 1) or 1)
239+
r = ds.Rows
240+
c = ds.Columns
241241
bpp = ds.BitsAllocated
242242

243243
dtype = pixel_dtype(ds)

0 commit comments

Comments
 (0)