@@ -148,154 +148,14 @@ STATIC mp_obj_t bitmapfilter_morph(size_t n_args, const mp_obj_t *pos_args, mp_m
148148 return args [ARG_bitmap ].u_obj ;
149149}
150150MP_DEFINE_CONST_FUN_OBJ_KW (bitmapfilter_morph_obj , 0 , bitmapfilter_morph );
151-
152- //|
153- //| def morph9(
154- //| bitmap: displayio.Bitmap,
155- //| weights: Sequence[int],
156- //| mul: Sequence[float] | None,
157- //| add: Sequence[float] | None,
158- //| mask: displayio.Bitmap | None = None,
159- //| threshold=False,
160- //| offset: int = 0,
161- //| invert: bool = False,
162- //| ) -> displayio.Bitmap:
163- //| """Convolve an image with a kernel
164- //|
165- //| This is like a combination of 9 calls to morph plus one call to mix. It's
166- //| so complicated and hard to explain that it must be good for something.
167- //|
168- //| The ``bitmap``, which must be in RGB565_SWAPPED format, is modified
169- //| according to the ``weights``. Then a scaling factor ``m`` and an
170- //| offset factor ``b`` are applied.
171- //|
172- //| The ``weights`` must be a tuple of integers. The length of the tuple
173- //| must be 9 times the square of an odd number, such as 81 or 225. The weights
174- //| are taken in groups of 9, with the first 3 giving the proportion of each of
175- //| R, G, and B that are mixed into the output R, the next three the
176- //| proportions mixed into the output G, and the last 3 the proportions that
177- //| are mixed into the output blue.
178- //|
179- //| ``mul`` is a sequence of 3 numbers to multiply the convolution pixel
180- //| results by. When not set it defaults to a value that will prevent scaling
181- //| in the convolution output.
182- //|
183- //| ``add`` is a sequence of 3 numbers giving a value to add to each
184- //| convolution pixel result. If unspecified or None, 0 is used for all 3 channels.
185- //|
186- //| ``mul`` basically allows you to do a global contrast adjustment and
187- //| add allows you to do a global brightness adjustment. Pixels that go
188- //| outside of the image mins and maxes for color channels will be
189- //| clipped.
190- //|
191- //| If you’d like to adaptive threshold the image on the output of the
192- //| filter you can pass ``threshold=True`` which will enable adaptive
193- //| thresholding of the image which sets pixels to one or zero based on a
194- //| pixel’s brightness in relation to the brightness of the kernel of pixels
195- //| around them. A negative ``offset`` value sets more pixels to 1 as you make
196- //| it more negative while a positive value only sets the sharpest contrast
197- //| changes to 1. Set ``invert`` to invert the binary image resulting output.
198- //|
199- //| ``mask`` is another image to use as a pixel level mask for the operation.
200- //| The mask should be an image the same size as the image being operated on.
201- //| Only pixels set to a non-zero value in the mask are modified.
202- //|
203- //| .. code-block:: python
204- //|
205- //| kernel_gauss_3 = [
206- //| 1, 2, 1,
207- //| 2, 4, 2,
208- //| 1, 2, 1]
209- //|
210- //| def blur(bitmap):
211- //| \"""Blur the bitmap with a 3x3 gaussian kernel\"""
212- //| bitmapfilter.morph(bitmap, kernel_gauss_3, 1/sum(kernel_gauss_3))
213- //| """
214- //|
215-
216151static mp_obj_t subscr (mp_obj_t o , int i ) {
217152 return mp_obj_subscr (o , MP_OBJ_NEW_SMALL_INT (i ), MP_OBJ_SENTINEL );
218153}
219154
220- static mp_obj_t subscr_maybe (mp_obj_t o , int i ) {
221- return o == mp_const_none ? o : subscr (o , i );
222- }
223-
224155static mp_float_t float_subscr (mp_obj_t o , int i ) {
225156 return mp_obj_get_float (subscr (o , i ));
226157}
227158
228- STATIC mp_float_t float_subscr_maybe (mp_obj_t seq_maybe , int i , mp_float_t defval ) {
229- if (seq_maybe == mp_const_none ) {
230- return defval ;
231- }
232- return float_subscr (seq_maybe , i );
233- }
234-
235- STATIC mp_obj_t bitmapfilter_morph9 (size_t n_args , const mp_obj_t * pos_args , mp_map_t * kw_args ) {
236- enum { ARG_bitmap , ARG_weights , ARG_mul , ARG_add , ARG_threshold , ARG_offset , ARG_invert , ARG_mask };
237- static const mp_arg_t allowed_args [] = {
238- { MP_QSTR_bitmap , MP_ARG_REQUIRED | MP_ARG_OBJ , { .u_obj = MP_OBJ_NULL } },
239- { MP_QSTR_weights , MP_ARG_REQUIRED | MP_ARG_OBJ , { .u_obj = MP_OBJ_NULL } },
240- { MP_QSTR_mul , MP_ARG_OBJ , { .u_obj = MP_ROM_NONE } },
241- { MP_QSTR_add , MP_ARG_OBJ , { .u_obj = MP_ROM_NONE } },
242- { MP_QSTR_threshold , MP_ARG_BOOL , { .u_bool = false } },
243- { MP_QSTR_offset , MP_ARG_INT , { .u_int = 0 } },
244- { MP_QSTR_invert , MP_ARG_BOOL , { .u_bool = false } },
245- { MP_QSTR_mask , MP_ARG_OBJ , { .u_obj = MP_ROM_NONE } },
246- };
247- mp_arg_val_t args [MP_ARRAY_SIZE (allowed_args )];
248- mp_arg_parse_all (n_args , pos_args , kw_args , MP_ARRAY_SIZE (allowed_args ), allowed_args , args );
249-
250- mp_arg_validate_type (args [ARG_bitmap ].u_obj , & displayio_bitmap_type , MP_QSTR_bitmap );
251- displayio_bitmap_t * bitmap = args [ARG_bitmap ].u_obj ;
252-
253- displayio_bitmap_t * mask = NULL ; // the mask bitmap
254- if (args [ARG_mask ].u_obj != mp_const_none ) {
255- mp_arg_validate_type (args [ARG_mask ].u_obj , & displayio_bitmap_type , MP_QSTR_mask );
256- mask = MP_OBJ_TO_PTR (args [ARG_mask ].u_obj );
257- }
258-
259- mp_float_t b [3 ] = {
260- float_subscr_maybe (args [ARG_add ].u_obj , 0 , 0 ),
261- float_subscr_maybe (args [ARG_add ].u_obj , 1 , 0 ),
262- float_subscr_maybe (args [ARG_add ].u_obj , 2 , 0 ),
263- };
264-
265- mp_obj_t weights = args [ARG_weights ].u_obj ;
266- mp_obj_t obj_len = mp_obj_len (weights );
267- if (obj_len == MP_OBJ_NULL || !mp_obj_is_small_int (obj_len )) {
268- mp_raise_ValueError_varg (MP_ERROR_TEXT ("%q must be of type %q, not %q" ), MP_QSTR_weights , MP_QSTR_Sequence , mp_obj_get_type (weights )-> name );
269- }
270-
271- size_t n_weights = MP_OBJ_SMALL_INT_VALUE (obj_len );
272-
273- size_t sq_n_weights = (int )MICROPY_FLOAT_C_FUN (sqrt )(n_weights / 9 );
274- if (sq_n_weights % 2 == 0 || sq_n_weights * sq_n_weights * 9 != n_weights ) {
275- mp_raise_ValueError (MP_ERROR_TEXT ("weights must be a sequence with 9 times an odd square number of elements (usually 81 or 225)" ));
276- }
277-
278- int iweights [n_weights ];
279- int weight_sum [3 ] = {0 , 0 , 0 };
280- for (size_t i = 0 ; i < n_weights ; i ++ ) {
281- mp_int_t j = mp_obj_get_int (mp_obj_subscr (weights , MP_OBJ_NEW_SMALL_INT (i ), MP_OBJ_SENTINEL ));
282- iweights [i ] = j ;
283- int target_channel = (i / 3 ) % 3 ;
284- weight_sum [target_channel ] += j ;
285- }
286-
287- mp_float_t m [3 ] = {
288- get_m (subscr_maybe (args [ARG_mul ].u_obj , 0 ), weight_sum [0 ]),
289- get_m (subscr_maybe (args [ARG_mul ].u_obj , 1 ), weight_sum [1 ]),
290- get_m (subscr_maybe (args [ARG_mul ].u_obj , 2 ), weight_sum [2 ])
291- };
292-
293- shared_module_bitmapfilter_morph9 (bitmap , mask , sq_n_weights / 2 , iweights , m , b ,
294- args [ARG_threshold ].u_bool , args [ARG_offset ].u_bool , args [ARG_invert ].u_bool );
295- return args [ARG_bitmap ].u_obj ;
296- }
297- MP_DEFINE_CONST_FUN_OBJ_KW (bitmapfilter_morph9_obj , 0 , bitmapfilter_morph9 );
298-
299159//| def mix(
300160//| bitmap: displayio.Bitmap, weights: Sequence[int], mask: displayio.Bitmap | None = None
301161//| ) -> displayio.Bitmap:
@@ -557,7 +417,6 @@ MP_DEFINE_CONST_FUN_OBJ_KW(bitmapfilter_false_color_obj, 0, bitmapfilter_false_c
557417STATIC const mp_rom_map_elem_t bitmapfilter_module_globals_table [] = {
558418 { MP_ROM_QSTR (MP_QSTR___name__ ), MP_ROM_QSTR (MP_QSTR_bitmapfilter ) },
559419 { MP_ROM_QSTR (MP_QSTR_morph ), MP_ROM_PTR (& bitmapfilter_morph_obj ) },
560- { MP_ROM_QSTR (MP_QSTR_morph9 ), MP_ROM_PTR (& bitmapfilter_morph9_obj ) },
561420 { MP_ROM_QSTR (MP_QSTR_mix ), MP_ROM_PTR (& bitmapfilter_mix_obj ) },
562421 { MP_ROM_QSTR (MP_QSTR_solarize ), MP_ROM_PTR (& bitmapfilter_solarize_obj ) },
563422 { MP_ROM_QSTR (MP_QSTR_false_color ), MP_ROM_PTR (& bitmapfilter_false_color_obj ) },
0 commit comments