2828 NODATA_RGBA ,
2929 OPAQUE ,
3030 TRANSPARENT ,
31+ ColorMap ,
3132 all_black_color_map ,
33+ colormap_from_colors ,
3234 get_color_palette ,
35+ greyscale_colormap ,
3336 palette_from_remote_colortable ,
34- remove_alpha ,
3537)
3638from hybig .exceptions import HyBIGError
3739from hybig .sizes import (
@@ -155,6 +157,7 @@ def create_browse_imagery(
155157 xml file.
156158
157159 """
160+ logger .info (message )
158161 output_driver = image_driver (message .format .mime )
159162 out_image_file = output_image_file (Path (input_file_path ), driver = output_driver )
160163 out_world_file = output_world_file (Path (input_file_path ), driver = output_driver )
@@ -171,18 +174,19 @@ def create_browse_imagery(
171174 color_palette = get_color_palette (
172175 in_dataset , source , item_color_palette
173176 )
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+ )
175180 elif rio_in_array .rio .count in (3 , 4 ):
176181 raster = convert_mulitband_to_raster (rio_in_array )
182+ color_map = None
183+ if output_driver == 'JPEG' :
184+ raster = raster [0 :3 , :, :]
177185 else :
178186 raise HyBIGError (
179187 f'incorrect number of bands for image: { rio_in_array .rio .count } '
180188 )
181189
182- raster , color_map = standardize_raster_for_writing (
183- raster , output_driver , rio_in_array .rio .count
184- )
185-
186190 grid_parameters = get_target_grid_parameters (message , rio_in_array )
187191 grid_parameter_list , tile_locators = create_tiled_output_parameters (
188192 grid_parameters
@@ -283,69 +287,64 @@ def original_dtype(data_array: DataArray) -> str | None:
283287def convert_singleband_to_raster (
284288 data_array : DataArray ,
285289 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 .
288292
289- Use a palette if provided otherwise return a greyscale image.
293+ Uses a palette if provided otherwise returns a greyscale image.
290294 """
291295 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 )
294298
295299
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 ."""
298302 band = data_array [0 , :, :]
299- cmap = matplotlib .colormaps ['Greys_r' ]
300- cmap .set_bad (NODATA_RGBA )
301303 norm = Normalize (vmin = np .nanmin (band ), vmax = np .nanmax (band ))
302- scalar_map = ScalarMappable (cmap = cmap , norm = norm )
303304
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
308307
309- return reshape_as_raster (rgba_image )
308+ # Set any missing to missing
309+ normalized_data [np .isnan (normalized_data )] = NODATA_IDX
310310
311+ grey_colormap = greyscale_colormap ()
312+ raster_data = np .expand_dims (np .round (normalized_data ).data , 0 )
313+ return raster_data , grey_colormap
311314
312- def convert_paletted_1band_to_raster (
315+
316+ def scale_paletted_1band (
313317 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+ """
316325 band = data_array [0 , :, :]
317326 levels = list (palette .pal .keys ())
318327 colors = [
319328 palette .color_to_color_entry (value , with_alpha = True )
320329 for value in palette .pal .values ()
321330 ]
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 )
329332
330333 # handle palette no data value
334+ nodata_color = (0 , 0 , 0 , 0 )
331335 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 )
341337
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
349348
350349
351350def image_driver (mime : str ) -> str :
@@ -355,81 +354,6 @@ def image_driver(mime: str) -> str:
355354 return 'PNG'
356355
357356
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-
433357def get_color_map_from_image (image : Image ) -> dict :
434358 """Get a writable color map
435359
@@ -444,6 +368,10 @@ def get_color_map_from_image(image: Image) -> dict:
444368 return color_map
445369
446370
371+ def get_colormap_from_scaled_colors (scaled_colors ) -> dict :
372+ pass
373+
374+
447375def get_aux_xml_filename (image_filename : Path ) -> Path :
448376 """Get aux.xml filenames."""
449377 return image_filename .with_suffix (image_filename .suffix + '.aux.xml' )
0 commit comments