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") | |