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.
This commit is contained in:
parent
9c2ed1c70c
commit
176c1ccac2
2 changed files with 69 additions and 25 deletions
30
run_tests.py
Normal file
30
run_tests.py
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
from pathlib import Path
|
||||
|
||||
import infiray_irg
|
||||
|
||||
test_files = sorted(Path('test_pictures').glob('*.irg'))
|
||||
|
||||
for f in test_files:
|
||||
try:
|
||||
coarse, fine, vis = infiray_irg.load(f.read_bytes(), print_debug_information=True)
|
||||
print('\033[93m', f'{f.name:>20}', 'Coldest pixel:', fine.min(), 'C', 'Hottest pixel:', fine.max(), 'C', '\033[0m')
|
||||
except Exception as e:
|
||||
print(f'Error parsing {f}')
|
||||
raise e
|
||||
|
||||
print()
|
||||
print('Header diffs:')
|
||||
|
||||
for offset, legend in {
|
||||
0: 'magc hlen coars_len yres xres flg0 unk1 zero fine unk2 jpeg_len_ yres xres emissivit fine_off1 fine_off2 distance_',
|
||||
64: ' unit gain'}.items():
|
||||
print(f'{" ":>20}', legend)
|
||||
test_headers = {f: f.read_bytes()[offset:offset+64] for f in test_files}
|
||||
header_idx_diffs = [len(set(header[i] for header in test_headers.values())) > 1 for i in range(64)]
|
||||
for f, header in test_headers.items():
|
||||
print(f'{str(f.name):>20}', ' '.join(
|
||||
''.join(
|
||||
(f'\033[91m{header[i]:02x}' if header_idx_diffs[i] else f'\033[0m{header[i]:02x}')
|
||||
for i in range(chunk, chunk+2))
|
||||
for chunk in range(0, 64, 2)
|
||||
)+'\033[0m')
|
||||
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue