Skip to content

Commit a27968d

Browse files
committed
Slight performance improvements of image processing
np.minimum / maximum are surprisingly slow!
1 parent 9021fd0 commit a27968d

File tree

1 file changed

+48
-13
lines changed

1 file changed

+48
-13
lines changed

adafruit_pycamera/imageprocessing.py

Lines changed: 48 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,37 @@
44
"""Routines for performing image manipulation"""
55

66
import struct
7+
from adafruit_ticks import ticks_ms, ticks_diff
78

9+
from micropython import const
810
import ulab.numpy as np
911

12+
# Optionally enable reporting of time taken inside tagged functions
13+
_DO_TIME_REPORT = const(0)
14+
15+
if _DO_TIME_REPORT:
16+
17+
def _timereport(func):
18+
"""Report time taken within the function"""
19+
name = str(func).split()[1]
20+
21+
def inner(*args, **kw):
22+
start = ticks_ms()
23+
try:
24+
return func(*args, **kw)
25+
finally:
26+
end = ticks_ms()
27+
duration = ticks_diff(end, start)
28+
print(f"{name}: {duration}ms")
29+
30+
return inner
31+
32+
else:
33+
34+
def _timereport(func):
35+
"""A do-nothing decorator for when timing report is not desired"""
36+
return func
37+
1038

1139
def _bytes_per_row(source_width: int) -> int:
1240
"""Internal function to determine bitmap bytes per row"""
@@ -83,11 +111,14 @@ def _array_cast(arr, dtype):
83111
return np.frombuffer(arr, dtype=dtype).reshape(arr.shape)
84112

85113

114+
@_timereport
86115
def bitmap_to_components_rgb565(bitmap):
87116
"""Convert a RGB565_BYTESWAPPED image to int16 components in the [0,255] inclusive range
88117
89118
This requires higher memory than uint8, but allows more arithmetic on pixel values;
90-
converting back to bitmap clamps values to the appropriate range.
119+
but values are masked (not clamped) back down to the 0-255 range, so while intermediate
120+
values can be -32768..32767 the values passed into bitmap_from_components_inplace_rgb565
121+
muts be 0..255
91122
92123
This only works on images whose width is a multiple of 2 pixels.
93124
"""
@@ -100,17 +131,20 @@ def bitmap_to_components_rgb565(bitmap):
100131
return r, g, b
101132

102133

134+
@_timereport
103135
def bitmap_from_components_inplace_rgb565(
104136
bitmap, r, g, b
105137
): # pylint: disable=invalid-name
106138
"""Update a bitmap in-place with new RGB values"""
107139
dest = _bitmap_as_array(bitmap)
108-
r = _array_cast(np.maximum(np.minimum(r, 255), 0), np.uint16)
109-
g = _array_cast(np.maximum(np.minimum(g, 255), 0), np.uint16)
110-
b = _array_cast(np.maximum(np.minimum(b, 255), 0), np.uint16)
111-
dest[:] = np.left_shift(r & 0xF8, 8)
112-
dest[:] |= np.left_shift(g & 0xFC, 3)
113-
dest[:] |= np.right_shift(b, 3)
140+
r = _array_cast(r, np.uint16)
141+
g = _array_cast(g, np.uint16)
142+
b = _array_cast(b, np.uint16)
143+
dest[:] = (
144+
np.left_shift(r & 0xF8, 8)
145+
| np.left_shift(g & 0xFC, 3)
146+
| np.right_shift(b & 0xF8, 3)
147+
)
114148
dest.byteswap(inplace=True)
115149
return bitmap
116150

@@ -125,13 +159,10 @@ def buffer_from_components_rgb888(r, g, b):
125159
r = _as_flat(r)
126160
g = _as_flat(g)
127161
b = _as_flat(b)
128-
r = np.maximum(np.minimum(r, 0x3F), 0)
129-
g = np.maximum(np.minimum(g, 0x3F), 0)
130-
b = np.maximum(np.minimum(b, 0x3F), 0)
131162
result = np.zeros(3 * len(r), dtype=np.uint8)
132-
result[2::3] = r
133-
result[1::3] = g
134-
result[0::3] = b
163+
result[2::3] = r & 0xFF
164+
result[1::3] = g & 0xFF
165+
result[0::3] = b & 0xFF
135166
return result
136167

137168

@@ -146,13 +177,15 @@ def symmetric_filter_inplace(data, coeffs, scale):
146177
column_filter_inplace(data, coeffs, scale)
147178

148179

180+
@_timereport
149181
def row_filter_inplace(data, coeffs, scale):
150182
"""Apply a filter to data in rows, changing it in place"""
151183
n_rows = data.shape[0]
152184
for i in range(n_rows):
153185
data[i, :] = _np_convolve_same(data[i, :], coeffs) // scale
154186

155187

188+
@_timereport
156189
def column_filter_inplace(data, coeffs, scale):
157190
"""Apply a filter to data in columns, changing it in place"""
158191
n_cols = data.shape[1]
@@ -169,6 +202,7 @@ def bitmap_symmetric_filter_inplace(bitmap, coeffs, scale):
169202
return bitmap_from_components_inplace_rgb565(bitmap, r, g, b)
170203

171204

205+
@_timereport
172206
def bitmap_channel_filter3_inplace(
173207
bitmap, r_func=lambda r, g, b: r, g_func=lambda r, g, b: g, b_func=lambda r, g, b: b
174208
):
@@ -182,6 +216,7 @@ def bitmap_channel_filter3_inplace(
182216
return bitmap_from_components_inplace_rgb565(bitmap, r, g, b)
183217

184218

219+
@_timereport
185220
def bitmap_channel_filter1_inplace(
186221
bitmap, r_func=lambda r: r, g_func=lambda g: g, b_func=lambda b: b
187222
):

0 commit comments

Comments
 (0)