Spaces:
Sleeping
Sleeping
| #!/usr/bin/env python3 | |
| # -*- coding: utf-8 -*- | |
| """ | |
| @author : Romain Graux | |
| @date : 2023 April 25, 11:59:06 | |
| @last modified : 2023 September 19, 11:18:36 | |
| """ | |
| from typing import Callable, Optional | |
| import re | |
| import imageio | |
| from collections import namedtuple | |
| import numpy as np | |
| PhysicalMetadata = namedtuple( | |
| "PhysicalMetadata", ["width", "height", "pixel_width", "pixel_height", "unit"] | |
| ) | |
| MetadataExtractor = Callable[[dict, int, int], Optional[PhysicalMetadata]] | |
| def extract_imagej_metadata( | |
| metadata: dict, width: int, height: int | |
| ) -> Optional[PhysicalMetadata]: | |
| try: | |
| ipw, iph, _ = metadata["resolution"] | |
| result = re.search(r"unit=(.+)", metadata["description"]) | |
| if not result: | |
| return None | |
| unit = result.group(1) | |
| return PhysicalMetadata(width, height, 1.0 / ipw, 1.0 / iph, unit.lower()) | |
| except (KeyError, AttributeError): | |
| return None | |
| def extract_resolution_metadata( | |
| metadata: dict, width: int, height: int | |
| ) -> Optional[PhysicalMetadata]: | |
| try: | |
| ipw, iph, _ = metadata["resolution"] | |
| # It looks like the resolution unit is not really reliable, so let's just assume nm | |
| unit = "nm" | |
| return PhysicalMetadata(width, height, 1.0 / ipw, 1.0 / iph, unit) | |
| except (KeyError, AttributeError): | |
| return None | |
| METADATA_EXTRACTORS: list[MetadataExtractor] = [ | |
| extract_imagej_metadata, | |
| extract_resolution_metadata, | |
| ] | |
| def normalize_metadata(metadata: PhysicalMetadata) -> PhysicalMetadata: | |
| conversion_factor = { | |
| "inch": 2.54e7, | |
| "m": 1e9, | |
| "dm": 1e8, | |
| "cm": 1e7, | |
| "mm": 1e6, | |
| "µm": 1e3, | |
| "nm": 1, | |
| } | |
| if metadata.unit not in conversion_factor: | |
| raise ValueError(f"Unknown unit: {metadata.unit}") | |
| factor = conversion_factor[metadata.unit] | |
| return PhysicalMetadata( | |
| metadata.width, | |
| metadata.height, | |
| metadata.pixel_width * factor, | |
| metadata.pixel_height * factor, | |
| "nm", | |
| ) | |
| def extract_physical_metadata(image_path: str, strict: bool = True) -> PhysicalMetadata: | |
| """ | |
| Extracts the physical metadata of an image by trying all available extractors. | |
| Raises ValueError if no extractor succeeds. | |
| """ | |
| with open(image_path, "rb") as f: | |
| data = f.read() | |
| reader = imageio.get_reader(data) | |
| metadata = reader.get_meta_data() | |
| h, w = reader.get_next_data().shape | |
| for extractor in METADATA_EXTRACTORS: | |
| result = extractor(metadata, w, h) | |
| if result is not None: | |
| return normalize_metadata(result) | |
| raise ValueError( | |
| "Failed to extract metadata from the image using any available method." | |
| ) | |
| def tiff_to_png(image, inplace=True): | |
| img = image if inplace else image.copy() | |
| if np.array(img.getdata()).max() <= 1: | |
| img = img.point(lambda p: p * 255) | |
| return img.convert("RGB") | |