From c7ada3668c77b8bac1025c22d6ab4dfc5d5c85d1 Mon Sep 17 00:00:00 2001 From: Piero Toffanin Date: Thu, 29 Jun 2023 14:10:53 +0200 Subject: [PATCH 1/2] Start adding thermal support for DJI M3T --- opendm/exiftool.py | 9 ++++++++- opendm/photo.py | 6 ++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/opendm/exiftool.py b/opendm/exiftool.py index 7dfe087ae..40542d3d6 100644 --- a/opendm/exiftool.py +++ b/opendm/exiftool.py @@ -2,6 +2,7 @@ import os import tempfile import base64 +import numpy as np from rasterio.io import MemoryFile from opendm.system import run from opendm import log @@ -36,7 +37,12 @@ def extract_raw_thermal_image_data(image_path): img = img[0][:,:,None] del j["RawThermalImage"] - + elif "ThermalData" in j: + thermal_data = base64.b64decode(j["ThermalData"][len("base64:"):]) + thermal_buf = np.frombuffer(thermal_data, dtype=np.int16) + # TODO: how to interpret these? + # https://exiftool.org/forum/index.php?topic=11401.45 + return extract_temperature_params_from(j), img else: raise Exception("Invalid JSON (not a list)") @@ -68,6 +74,7 @@ def _convert(v): def extract_temperature_params_from(tags): # Defaults + meta = { "Emissivity": float, "ObjectDistance": unit("m"), diff --git a/opendm/photo.py b/opendm/photo.py index 3e75a2501..61b01bd6c 100644 --- a/opendm/photo.py +++ b/opendm/photo.py @@ -481,6 +481,12 @@ def parse_exif_values(self, _path_file): self.capture_uuid = matches.group(1) self.band_name = band_aliases.get(matches.group(2), matches.group(2)) + # Some DJI models do not have a band name but have an image source field + if self.camera_make.lower() == 'dji': + image_source = self.get_xmp_tag(xtags, '@drone-dji:ImageSource') + if self.band_name == 'RGB' and isinstance(image_source, str) and image_source.lower() == "infraredcamera": + self.band_name = 'LWIR' + # Sanitize band name since we use it in folder paths self.band_name = re.sub('[^A-Za-z0-9]+', '', self.band_name) From 03efa6de0d23142c3c8e3e150eb969b2d2dec00f Mon Sep 17 00:00:00 2001 From: Piero Toffanin Date: Fri, 28 Jun 2024 13:44:20 +0000 Subject: [PATCH 2/2] Temperature conversion --- SuperBuild/cmake/External-ExifTool.cmake | 2 +- opendm/exiftool.py | 52 ++++++++++++++++++++++-- 2 files changed, 49 insertions(+), 5 deletions(-) diff --git a/SuperBuild/cmake/External-ExifTool.cmake b/SuperBuild/cmake/External-ExifTool.cmake index 50291203e..ac59bd3d3 100644 --- a/SuperBuild/cmake/External-ExifTool.cmake +++ b/SuperBuild/cmake/External-ExifTool.cmake @@ -28,7 +28,7 @@ externalproject_add(${_proj_name} SOURCE_DIR ${SB_SOURCE_DIR}/${_proj_name} #--Download step-------------- DOWNLOAD_DIR ${SB_DOWNLOAD_DIR} - URL https://github.com/exiftool/exiftool/archive/refs/tags/12.62.zip + URL https://github.com/exiftool/exiftool/archive/refs/tags/12.70.zip UPDATE_COMMAND "" CONFIGURE_COMMAND "" BUILD_IN_SOURCE 1 diff --git a/opendm/exiftool.py b/opendm/exiftool.py index 40542d3d6..a538aa6da 100644 --- a/opendm/exiftool.py +++ b/opendm/exiftool.py @@ -39,9 +39,40 @@ def extract_raw_thermal_image_data(image_path): del j["RawThermalImage"] elif "ThermalData" in j: thermal_data = base64.b64decode(j["ThermalData"][len("base64:"):]) - thermal_buf = np.frombuffer(thermal_data, dtype=np.int16) + thermal_data_buf = np.frombuffer(thermal_data, dtype=np.int16) + + thermal_calibration = base64.b64decode(j["ThermalCalibration"][len("base64:"):]) + thermal_calibration_buf = np.frombuffer(thermal_calibration, dtype=np.int16) + # TODO: how to interpret these? # https://exiftool.org/forum/index.php?topic=11401.45 + print(thermal_data_buf.shape) + print(thermal_calibration_buf.shape) + print(" ".join("%02x" % b for b in thermal_data_buf[0:10])) + print(thermal_data_buf[0:10]) + print(thermal_calibration_buf[0:10]) + + # temperatures = np.right_shift(thermal_data_buf, 2).astype(np.float32) + # temperatures *= 0.0625 + # temperatures -= 273.15 + img = thermal_data_buf.reshape((512, 640)) # notice row, column format + + + # from PIL import Image + # img = Image.fromarray(im) + + # temperatures = np.right_shift(thermal_data_buf, 2).astype(np.float32) + # temperatures *= 0.0625 + # temperatures -= 273.15 + + # rows = 512 + # cols = 640 + # im = temperatures.reshape((rows, cols)) # notice row, column format + + # dest_path = '/datasets/dji_thermal/out.tiff' + # img_thermal = Image.fromarray(im) + # img_thermal.save(dest_path) + return extract_temperature_params_from(j), img else: @@ -90,12 +121,25 @@ def extract_temperature_params_from(tags): "PlanckR2": float, } + aliases = { + "AtmosphericTemperature": ["AmbientTemperature"], + "ReflectedApparentTemperature": ["ReflectedTemperature"], +# "IRWindowTemperature": ["ReflectedApparentTemperature"], #fallback + } + params = {} for m in meta: - if m not in tags: - # All or nothing + keys = [m] + keys += aliases.get(m, []) + val = None + for k in keys: + if k in tags: + val = (meta[m])(tags[k]) + break + if val is None: raise Exception("Cannot find %s in tags" % m) - params[m] = (meta[m])(tags[m]) + + params[m] = val return params \ No newline at end of file