28
28
NODATA_RGBA ,
29
29
OPAQUE ,
30
30
TRANSPARENT ,
31
+ ColorMap ,
31
32
all_black_color_map ,
33
+ colormap_from_colors ,
32
34
get_color_palette ,
35
+ greyscale_colormap ,
33
36
palette_from_remote_colortable ,
34
- remove_alpha ,
35
37
)
36
38
from hybig .exceptions import HyBIGError
37
39
from hybig .sizes import (
@@ -155,6 +157,7 @@ def create_browse_imagery(
155
157
xml file.
156
158
157
159
"""
160
+ logger .info (message )
158
161
output_driver = image_driver (message .format .mime )
159
162
out_image_file = output_image_file (Path (input_file_path ), driver = output_driver )
160
163
out_world_file = output_world_file (Path (input_file_path ), driver = output_driver )
@@ -171,18 +174,19 @@ def create_browse_imagery(
171
174
color_palette = get_color_palette (
172
175
in_dataset , source , item_color_palette
173
176
)
174
- raster = convert_singleband_to_raster (rio_in_array , color_palette )
177
+ raster , color_map = convert_singleband_to_raster (
178
+ rio_in_array , color_palette
179
+ )
175
180
elif rio_in_array .rio .count in (3 , 4 ):
176
181
raster = convert_mulitband_to_raster (rio_in_array )
182
+ color_map = None
183
+ if output_driver == 'JPEG' :
184
+ raster = raster [0 :3 , :, :]
177
185
else :
178
186
raise HyBIGError (
179
187
f'incorrect number of bands for image: { rio_in_array .rio .count } '
180
188
)
181
189
182
- raster , color_map = standardize_raster_for_writing (
183
- raster , output_driver , rio_in_array .rio .count
184
- )
185
-
186
190
grid_parameters = get_target_grid_parameters (message , rio_in_array )
187
191
grid_parameter_list , tile_locators = create_tiled_output_parameters (
188
192
grid_parameters
@@ -283,69 +287,64 @@ def original_dtype(data_array: DataArray) -> str | None:
283
287
def convert_singleband_to_raster (
284
288
data_array : DataArray ,
285
289
color_palette : ColorPalette | None = None ,
286
- ) -> ndarray :
287
- """Convert input dataset to a 4 band raster image.
290
+ ) -> tuple [ ndarray , ColorMap ] :
291
+ """Convert input dataset to a 1- band palettized image with colormap .
288
292
289
- Use a palette if provided otherwise return a greyscale image.
293
+ Uses a palette if provided otherwise returns a greyscale image.
290
294
"""
291
295
if color_palette is None :
292
- return convert_gray_1band_to_raster (data_array )
293
- return convert_paletted_1band_to_raster (data_array , color_palette )
296
+ return scale_grey_1band (data_array )
297
+ return scale_paletted_1band (data_array , color_palette )
294
298
295
299
296
- def convert_gray_1band_to_raster (data_array : DataArray ) -> ndarray :
297
- """Convert a 1-band raster without a color association ."""
300
+ def scale_grey_1band (data_array : DataArray ) -> tuple [ ndarray , ColorMap ] :
301
+ """Normalize input array and return scaled data with greyscale ColorMap ."""
298
302
band = data_array [0 , :, :]
299
- cmap = matplotlib .colormaps ['Greys_r' ]
300
- cmap .set_bad (NODATA_RGBA )
301
303
norm = Normalize (vmin = np .nanmin (band ), vmax = np .nanmax (band ))
302
- scalar_map = ScalarMappable (cmap = cmap , norm = norm )
303
304
304
- rgba_image = np .zeros ((* band .shape , 4 ), dtype = 'uint8' )
305
- for row_no in range (band .shape [0 ]):
306
- rgba_image_slice = scalar_map .to_rgba (band [row_no , :], bytes = True )
307
- rgba_image [row_no , :, :] = rgba_image_slice
305
+ # Scale input data from 0 to 254
306
+ normalized_data = norm (band ) * 254.0
308
307
309
- return reshape_as_raster (rgba_image )
308
+ # Set any missing to missing
309
+ normalized_data [np .isnan (normalized_data )] = NODATA_IDX
310
310
311
+ grey_colormap = greyscale_colormap ()
312
+ raster_data = np .expand_dims (np .round (normalized_data ).data , 0 )
313
+ return raster_data , grey_colormap
311
314
312
- def convert_paletted_1band_to_raster (
315
+
316
+ def scale_paletted_1band (
313
317
data_array : DataArray , palette : ColorPalette
314
- ) -> ndarray :
315
- """Convert a 1 band image with palette into a rgba raster image."""
318
+ ) -> tuple [ndarray , ColorMap ]:
319
+ """Scale a 1-band image with palette into modified image and associated color_map.
320
+
321
+ Use the palette's levels and values, transform the input data_array into
322
+ the correct levels indexed from 0-255 return the scaled array along side of
323
+ a colormap corresponding to the new levels.
324
+ """
316
325
band = data_array [0 , :, :]
317
326
levels = list (palette .pal .keys ())
318
327
colors = [
319
328
palette .color_to_color_entry (value , with_alpha = True )
320
329
for value in palette .pal .values ()
321
330
]
322
- scaled_colors = [
323
- (r / 255.0 , g / 255.0 , b / 255.0 , a / 255.0 ) for r , g , b , a in colors
324
- ]
325
-
326
- cmap , norm = matplotlib .colors .from_levels_and_colors (
327
- levels , scaled_colors , extend = 'max'
328
- )
331
+ norm = matplotlib .colors .BoundaryNorm (levels , len (levels ) - 1 )
329
332
330
333
# handle palette no data value
334
+ nodata_color = (0 , 0 , 0 , 0 )
331
335
if palette .ndv is not None :
332
- nodata_colors = palette .color_to_color_entry (palette .ndv , with_alpha = True )
333
- cmap .set_bad (
334
- (
335
- nodata_colors [0 ] / 255.0 ,
336
- nodata_colors [1 ] / 255.0 ,
337
- nodata_colors [2 ] / 255.0 ,
338
- nodata_colors [3 ] / 255.0 ,
339
- )
340
- )
336
+ nodata_color = palette .color_to_color_entry (palette .ndv , with_alpha = True )
341
337
342
- scalar_map = matplotlib .cm .ScalarMappable (norm = norm , cmap = cmap )
343
- rgba_image = np .zeros ((* band .shape , 4 ), dtype = 'uint8' )
344
- for row_no in range (band .shape [0 ]):
345
- rgba_image [row_no , :, :] = scalar_map .to_rgba (
346
- np .ma .masked_invalid (band [row_no , :]), bytes = True
347
- )
348
- return reshape_as_raster (rgba_image )
338
+ colors = [* colors , nodata_color ]
339
+
340
+ scaled_band = norm (band )
341
+
342
+ # Set underflow and nan values to nodata
343
+ scaled_band [scaled_band == - 1 ] = len (colors ) - 1
344
+ scaled_band [np .isnan (band )] = len (colors ) - 1
345
+
346
+ color_map = colormap_from_colors (colors )
347
+ return np .array (np .expand_dims (scaled_band .data , 0 ), dtype = "uint8" ), color_map
349
348
350
349
351
350
def image_driver (mime : str ) -> str :
@@ -355,81 +354,6 @@ def image_driver(mime: str) -> str:
355
354
return 'PNG'
356
355
357
356
358
- def standardize_raster_for_writing (
359
- raster : ndarray ,
360
- driver : str ,
361
- band_count : int ,
362
- ) -> tuple [ndarray , dict | None ]:
363
- """Standardize raster data for writing to browse image.
364
-
365
- Args:
366
- raster: Input raster data array
367
- driver: Output image format ('JPEG' or 'PNG')
368
- band_count: Number of bands in original input data
369
-
370
- The function handles two special cases:
371
- - JPEG output with 4-band data -> Drop alpha channel and return 3-band RGB
372
- - PNG output with single-band data -> Convert to paletted format
373
-
374
- Returns:
375
- tuple: (prepared_raster, color_map) where:
376
- - prepared_raster is the processed ndarray
377
- - color_map is either None or a dict mapping palette indices to RGBA values
378
-
379
-
380
- """
381
- if driver == 'JPEG' and raster .shape [0 ] == 4 :
382
- return raster [0 :3 , :, :], None
383
-
384
- if driver == 'PNG' and band_count == 1 :
385
- # Only palettize single band input data that has been converted to an
386
- # RGBA raster.
387
- return palettize_raster (raster )
388
-
389
- return raster , None
390
-
391
-
392
- def palettize_raster (raster : ndarray ) -> tuple [ndarray , dict ]:
393
- """Convert an RGB or RGBA image into a 1band image and palette.
394
-
395
- Converts a 3 or 4 band np raster into a PIL image.
396
- Quantizes the image into a 1band raster with palette
397
-
398
- Transparency is handled by first removing the Alpha layer and creating
399
- quantized raster from just the RGB layers. Next the Alpha layer values are
400
- treated as either transparent or opaque and any transparent values are
401
- written to the final raster as 254 and add the mapped RGBA value to the
402
- color palette.
403
- """
404
- # reserves index 255 for transparent and off grid fill values
405
- # 0 to 254
406
- max_colors = 255
407
- rgb_raster , alpha = remove_alpha (raster )
408
-
409
- multiband_image = Image .fromarray (reshape_as_image (rgb_raster ))
410
- quantized_image = multiband_image .quantize (colors = max_colors )
411
-
412
- color_map = get_color_map_from_image (quantized_image )
413
-
414
- quantized_array , color_map = add_alpha (alpha , np .array (quantized_image ), color_map )
415
-
416
- one_band_raster = np .expand_dims (quantized_array , 0 )
417
- return one_band_raster , color_map
418
-
419
-
420
- def add_alpha (
421
- alpha : ndarray | None , quantized_array : ndarray , color_map : dict
422
- ) -> tuple [ndarray , dict ]:
423
- """If the input data had alpha values, manually set the quantized_image
424
- index to the transparent index in those places.
425
- """
426
- if alpha is not None and np .any (alpha != OPAQUE ):
427
- # Set any alpha to the transparent index value
428
- quantized_array = np .where (alpha != OPAQUE , NODATA_IDX , quantized_array )
429
- color_map [NODATA_IDX ] = NODATA_RGBA
430
- return quantized_array , color_map
431
-
432
-
433
357
def get_color_map_from_image (image : Image ) -> dict :
434
358
"""Get a writable color map
435
359
@@ -444,6 +368,10 @@ def get_color_map_from_image(image: Image) -> dict:
444
368
return color_map
445
369
446
370
371
+ def get_colormap_from_scaled_colors (scaled_colors ) -> dict :
372
+ pass
373
+
374
+
447
375
def get_aux_xml_filename (image_filename : Path ) -> Path :
448
376
"""Get aux.xml filenames."""
449
377
return image_filename .with_suffix (image_filename .suffix + '.aux.xml' )
0 commit comments