@@ -70,29 +70,32 @@ def _np_convolve_same(arr, coeffs):
70
70
EIGHT_BITS = 0b11111111
71
71
72
72
73
- def bitmap_as_array (bitmap ):
73
+ def _bitmap_as_array (bitmap ):
74
74
"""Create an array object that accesses the bitmap data"""
75
75
if bitmap .width % 2 :
76
76
raise ValueError ("Can only work on even-width bitmaps" )
77
77
return np .frombuffer (bitmap , dtype = np .uint16 ).reshape ((bitmap .height , bitmap .width ))
78
78
79
79
80
- def array_cast (arr , dtype ):
80
+ def _array_cast (arr , dtype ):
81
81
"""Cast an array to a given type and shape. The new type must match the original
82
82
type's size in bytes."""
83
83
return np .frombuffer (arr , dtype = dtype ).reshape (arr .shape )
84
84
85
85
86
86
def bitmap_to_components_rgb565 (bitmap ):
87
- """Convert a RGB65_BYTESWAPPED image to int16 components in the [0,255] inclusive range
87
+ """Convert a RGB565_BYTESWAPPED image to int16 components in the [0,255] inclusive range
88
88
89
89
This requires higher memory than uint8, but allows more arithmetic on pixel values;
90
- converting back to bitmap clamps values to the appropriate range."""
91
- arr = bitmap_as_array (bitmap )
90
+ converting back to bitmap clamps values to the appropriate range.
91
+
92
+ This only works on images whose width is a multiple of 2 pixels.
93
+ """
94
+ arr = _bitmap_as_array (bitmap )
92
95
arr .byteswap (inplace = True )
93
- r = array_cast (np .right_shift (arr , 8 ) & 0xF8 , np .int16 )
94
- g = array_cast (np .right_shift (arr , 3 ) & 0xFC , np .int16 )
95
- b = array_cast (np .left_shift (arr , 3 ) & 0xF8 , np .int16 )
96
+ r = _array_cast (np .right_shift (arr , 8 ) & 0xF8 , np .int16 )
97
+ g = _array_cast (np .right_shift (arr , 3 ) & 0xFC , np .int16 )
98
+ b = _array_cast (np .left_shift (arr , 3 ) & 0xF8 , np .int16 )
96
99
arr .byteswap (inplace = True )
97
100
return r , g , b
98
101
@@ -101,27 +104,27 @@ def bitmap_from_components_inplace_rgb565(
101
104
bitmap , r , g , b
102
105
): # pylint: disable=invalid-name
103
106
"""Update a bitmap in-place with new RGB values"""
104
- dest = bitmap_as_array (bitmap )
105
- r = array_cast (np .maximum (np .minimum (r , 255 ), 0 ), np .uint16 )
106
- g = array_cast (np .maximum (np .minimum (g , 255 ), 0 ), np .uint16 )
107
- b = array_cast (np .maximum (np .minimum (b , 255 ), 0 ), np .uint16 )
107
+ 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 )
108
111
dest [:] = np .left_shift (r & 0xF8 , 8 )
109
112
dest [:] |= np .left_shift (g & 0xFC , 3 )
110
113
dest [:] |= np .right_shift (b , 3 )
111
114
dest .byteswap (inplace = True )
112
115
return bitmap
113
116
114
117
115
- def as_flat (arr ):
116
- """Flatten an array, ensuring no copy is made"""
118
+ def _as_flat (arr ):
119
+ """Internal routine to flatten an array, ensuring no copy is made"""
117
120
return np .frombuffer (arr , arr .dtype )
118
121
119
122
120
123
def buffer_from_components_rgb888 (r , g , b ):
121
124
"""Convert the individual color components to a single RGB888 buffer in memory"""
122
- r = as_flat (r )
123
- g = as_flat (g )
124
- b = as_flat (b )
125
+ r = _as_flat (r )
126
+ g = _as_flat (g )
127
+ b = _as_flat (b )
125
128
r = np .maximum (np .minimum (r , 0x3F ), 0 )
126
129
g = np .maximum (np .minimum (g , 0x3F ), 0 )
127
130
b = np .maximum (np .minimum (b , 0x3F ), 0 )
@@ -139,21 +142,26 @@ def symmetric_filter_inplace(data, coeffs, scale):
139
142
many common kinds of image filters such as blur, sharpen, and edge detect.
140
143
141
144
Normally, scale is sum(coeffs)."""
142
- # First run the filter across each row
145
+ row_filter_inplace (data , coeffs , scale )
146
+ column_filter_inplace (data , coeffs , scale )
147
+
148
+
149
+ def row_filter_inplace (data , coeffs , scale ):
150
+ """Apply a filter to data in rows, changing it in place"""
143
151
n_rows = data .shape [0 ]
144
152
for i in range (n_rows ):
145
153
data [i , :] = _np_convolve_same (data [i , :], coeffs ) // scale
146
154
147
- # Run the filter across each column
155
+
156
+ def column_filter_inplace (data , coeffs , scale ):
157
+ """Apply a filter to data in columns, changing it in place"""
148
158
n_cols = data .shape [1 ]
149
159
for i in range (n_cols ):
150
160
data [:, i ] = _np_convolve_same (data [:, i ], coeffs ) // scale
151
161
152
- return data
153
-
154
162
155
163
def bitmap_symmetric_filter_inplace (bitmap , coeffs , scale ):
156
- """Apply a symmetric filter to an image, updating the original image"""
164
+ """Apply the same filter to an image by rows and then by columns , updating the original image"""
157
165
r , g , b = bitmap_to_components_rgb565 (bitmap )
158
166
symmetric_filter_inplace (r , coeffs , scale )
159
167
symmetric_filter_inplace (g , coeffs , scale )
@@ -262,14 +270,31 @@ def sharpen(bitmap):
262
270
)
263
271
264
272
273
+ def _edge_filter_component (data , coefficients ):
274
+ """Internal filter to apply H+V edge detection to an image component"""
275
+ data_copy = data [:]
276
+ row_filter_inplace (data , coefficients , scale = 1 )
277
+ column_filter_inplace (data_copy , coefficients , scale = 1 )
278
+ data += data_copy
279
+ data += 128
280
+
281
+
265
282
def edgedetect (bitmap ):
266
283
"""Run an edge detection routine on a bitmap"""
267
284
coefficients = np .array ([- 1 , 0 , 1 ])
268
285
r , g , b = bitmap_to_components_rgb565 (bitmap )
269
- symmetric_filter_inplace (r , coefficients , scale = 1 )
270
- r += 128
271
- symmetric_filter_inplace (g , coefficients , scale = 1 )
272
- g += 128
273
- symmetric_filter_inplace (b , coefficients , scale = 1 )
274
- b += 128
286
+ _edge_filter_component (r , coefficients )
287
+ _edge_filter_component (g , coefficients )
288
+ _edge_filter_component (b , coefficients )
275
289
return bitmap_from_components_inplace_rgb565 (bitmap , r , g , b )
290
+
291
+
292
+ def edgedetect_greyscale (bitmap ):
293
+ """Run an edge detection routine on a bitmap in greyscale"""
294
+ coefficients = np .array ([- 1 , 0 , 1 ])
295
+ r , g , b = bitmap_to_components_rgb565 (bitmap )
296
+ luminance = np .right_shift (38 * r + 75 * g + 15 * b , 7 )
297
+ _edge_filter_component (luminance , coefficients )
298
+ return bitmap_from_components_inplace_rgb565 (
299
+ bitmap , luminance , luminance , luminance
300
+ )
0 commit comments