summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorjaseg <git@jaseg.de>2025-02-05 20:27:59 +0100
committerjaseg <git@jaseg.de>2025-02-05 20:27:59 +0100
commit176c1ccac2a2c758ac3a5d3922e05a1b936360e5 (patch)
tree703ee826b3b02cb7e676a5964e30f94d5e396131 /src
parent9c2ed1c70c2c704e6140c34c2f216865c6f12b0d (diff)
downloadinfiray_irg_py-176c1ccac2a2c758ac3a5d3922e05a1b936360e5.tar.gz
infiray_irg_py-176c1ccac2a2c758ac3a5d3922e05a1b936360e5.tar.bz2
infiray_irg_py-176c1ccac2a2c758ac3a5d3922e05a1b936360e5.zip
Fix offset issues.
This commit is tested against all test files I have available. We tried to parse the temperature array offset from the file to match that one Autel device, but that ended up breaking other stuff. It seems the offset is not explicitly recorded. TODO: Figure out if they are using 273.0 or 273.15 K.
Diffstat (limited to 'src')
-rw-r--r--src/infiray_irg.py64
1 files changed, 39 insertions, 25 deletions
diff --git a/src/infiray_irg.py b/src/infiray_irg.py
index c6a583a..400febb 100644
--- a/src/infiray_irg.py
+++ b/src/infiray_irg.py
@@ -6,7 +6,7 @@ import io
__version__ = "1.4.0"
-def load(data):
+def load(data, print_debug_information=False):
def consume(n):
nonlocal data
out, data = data[:n], data[n:]
@@ -14,7 +14,18 @@ def load(data):
raise ValueError(f'File is truncated, tried to read {n} bytes, but only {len(out)} bytes remain.')
return out
- header = consume(128)
+ header = consume(4)
+ _magic, header_len = struct.unpack('<HH', header)
+ header += consume(header_len - 4)
+
+ if print_debug_information:
+ import binascii
+ c200_reference_header = binascii.unhexlify('caac800000c000000001c00000008001000001c00001738300000001c0001c25000010a8290010a82900c4090000a00f000010270000000000001027000000000000000000000204000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000acca')
+ print('[infiray_irg debug] Header hex:', binascii.hexlify(header).decode()[:512])
+ print('[infiray_irg debug] C200 diff:', ''.join(
+ (f'\033[0m{b:02x}' if a == b else f'\033[91m{b:02x}') for a, b in zip(header, c200_reference_header)
+ ) + '\033[0m')
+
for model, match in {
'c201': bytes([0xca, 0xac]),
'other': bytes([0xba, 0xab]),
@@ -25,40 +36,48 @@ def load(data):
else:
raise ValueError(f'Header magic not found. Got header: {header[0]:02x} {header[1]:02x}')
- _unk0, coarse_section_length, y_res, x_res,\
+ coarse_section_length, y_res, x_res,\
flag0, _unk1, _zero1, fine_offset, _unk2, jpeg_length,\
- y_res_2, x_res_2, _unk3, = struct.unpack('<HIHHHHHHHIHHI', header[2:34])
+ y_res_2, x_res_2, emissivity = struct.unpack('<IHHHHHHHIHHI', header[4:34])
+ emissivity /= 1e4
- fine_temp_offset1, fine_temp_offset2, *rest, high_gain_mode_flag = struct.unpack('<11I', header[34:78])
+ # unit_flag indicates which temperature unit the UI was using. It does not seem to affect the image data in this
+ # file.
+ fine_temp_offset1, fine_temp_offset2, distance, *rest, unit_flag, high_gain_mode_flag = struct.unpack('<9IHHI', header[34:78])
+ distance /= 1e4
if fine_temp_offset1 != fine_temp_offset2:
warnings.warn(f'File lists two different zero offsets for the fine image data {fine_temp_offset1} and {fine_temp_offset2}. Resulting radiometric data might be offset. Please report this with an example file to code@jaseg.de.')
- fine_temp_offset = fine_temp_offset1 / 10000
-
-# import textwrap
-# print(textwrap.dedent(f'''
-# {_unk0=}, {coarse_section_length=}, {y_res=}, {x_res=},
-# {flag0=}, {_unk1=}, {_zero1=}, {fine_offset=}, {_unk2=}, {jpeg_length=},
-# {y_res_2=}, {x_res_2=}, {_unk3=}
-# {fine_temp_offset1=} {fine_temp_offset1=} {rest=}, {high_gain_mode_flag=}'''))
+ if print_debug_information:
+ print('[infiray_irg debug] Matched model:', model)
+ import textwrap
+ print(textwrap.dedent(f'''
+ [infiray_irg debug] {header_len=}, {coarse_section_length=}, {y_res=}, {x_res=},
+ [infiray_irg debug] {flag0=}, {_unk1=}, {_zero1=}, {fine_offset=}, {_unk2=}, {jpeg_length=},
+ [infiray_irg debug] {y_res_2=}, {x_res_2=}, {emissivity=} {distance=}
+ [infiray_irg debug] {fine_temp_offset1=} {fine_temp_offset1=} {rest=}, {high_gain_mode_flag=} {unit_flag=}'''))
if x_res*y_res != coarse_section_length:
raise ValueError('Resolution mismatch in header')
vis_jpg = None
+ coarse_img = np.frombuffer(consume(coarse_section_length), dtype=np.uint8).reshape((y_res, x_res))
+ fine_img = np.frombuffer(consume(x_res*y_res*2), dtype=np.uint16).reshape((y_res, x_res))
+
if model == 'c201':
if header[-2:] != bytes([0xac,0xca]):
raise ValueError(f'Header end marker not found. Got header: {header[-2]:02x} {header[-1]:02x}')
- coarse_img = np.frombuffer(consume(coarse_section_length), dtype=np.uint8).reshape((y_res, x_res))
- fine_img = np.frombuffer(consume(x_res*y_res*2), dtype=np.uint16).reshape((y_res, x_res))
if flag0 == 1: # Seen in Autel Robotics Evo II Dual 640T V3 file
- fine_img = (fine_img / 64) - fine_temp_offset
+ fine_img = (fine_img / 64) - 25.0
else: # C201 files
# 1/16th Kelvin steps
- fine_img = (fine_img / 16) - fine_temp_offset
+ fine_img = (fine_img / 16) - 273.15
+
+ # The offset for low gain mode images is a bit unclear. It seems all readings around room temperature and
+ # below in low gain mode are kind of garbage.
if jpeg_length > 0:
# I have seen a file from an Autel Robotics Evo II Dual 640T V3 that looks like a C201 file, but lacks the
@@ -66,20 +85,15 @@ def load(data):
vis_jpg = Image.open(io.BytesIO(consume(jpeg_length)))
elif model == 'other':
- if header[-2:] != bytes([0xab,0xba]):
+ if header[-2:] != bytes([0xac,0xca]):
raise ValueError(f'Header end marker not found. Got header: {header[-2]:02x} {header[-1]:02x}')
- coarse_img = np.frombuffer(consume(coarse_section_length), dtype=np.uint8).reshape((y_res, x_res))
# 0.1 Kelvin steps
- fine_img = np.frombuffer(consume(x_res*y_res*2), dtype=np.uint16).reshape((y_res, x_res))
- fine_img = fine_img / 10 - fine_temp_offset
+ fine_img = fine_img / 10 - 273.15
vis_jpg = Image.open(io.BytesIO(data))
else:
- header += consume(128)
- coarse_img = np.frombuffer(consume(coarse_section_length), dtype=np.uint8).reshape((y_res, x_res))
- fine_img = np.frombuffer(consume(x_res*y_res*2), dtype=np.uint16).reshape((y_res, x_res))
- fine_img = fine_img / 10 - fine_temp_offset
+ fine_img = fine_img / 10 - 273.2
# In my example file, data now contains the JSON '{"roi":[]}' and no JPG. We ignore that.