Hello !
I don't know if I post my question to the right place... but I need some help and with a little chance.... ;-)
I have a FLIR i7 and I need to import directly some informations in my python script for thermal photos. Now, I get these informations calling
exiftool -FLIR:PlanckR1 -FLIR:PlanckR2 -FLIR:PlanckB -FLIR:PlanckO -FLIR:PlanckF -FLIR:RawValueMedian -FLIR:RawValueRange -FLIR:ReflectedApparentTemperature -FLIR:Emissivity -s3 -q nomphoto
and I also get raw thermal data importing raw.png created with
exiftool -b -RawThermalImage nomphoto | convert - gray:- | convert -depth 16 -endian msb -size 140x140 gray:- raw.png
but I would like to have no call to exiftool to have a standalone program for my students.
Do you know how can I do ? I looked into FLIR.pm of exiftool without success. I don't know at what adresses to look to get informations (position etc...).
Thanks in advance !
Sorry if I'm really in a wrong place, and also for my english (I'm french... ;-) )
I don't think I can be any help here. FLIR.pm is the best documentation I know for the format of FLIR metadata.
- Phil
Thanks for the answer.
When I look into FLIR.pm, I can see, for example at line 297 :
0x58 => { Name => 'PlanckR1', %float8g }, #1
With exiftool -v5, I get (for the same PlanckR1) :
| | PlanckR1 = 12767.2041015625
| | - Tag 0x0058 (4 bytes, float[1]):
| | 4f44: d1 7c 47 46
I understand that 0x58 is not the absolute address for bytes for PlanckR1 value. It's relative ? How can I get an absolute address ? What about 4f44 I see with exiftool ?
I'm newbie for binary decoding and I think I miss the way to get absolute positions (they change from one file to another). I just need to get the right byte position for PlanckR1 (I think I can get the other informations after...).
Thanks if you can help me and, even if you can't, thanks for exiftool !
First you need to parse the JPEG file structure to find the FLIR APP1 segment, then see the ProcessFLIR subroutine in FLIR.pm for how to parse this data. The comments in the code give details about the structure.
- Phil
P.S. I will be splitting this thread into a separate topic and moving it into the Metadata board.
Ok. I will give it a try...
Long life to exiftool ! ;-)
as simple entry point you can look in the comments of my first post
https://exiftool.org/forum/index.php/topic,4898.msg23497.html#msg23497
here I used awk for parsing the FLIR APP1 segment and the sub segment "0x01 => { Name => 'RawData',..."
use this document for understanding the fff format
Thank you very much !
Success !!!!
My problem is solved with your help. Here is my pythonic way to extract a numpy array with temperatures of a photo from the FLIR i7 (hope this helps !) :
import numpy as np
from math import pi
from PIL import Image
import io
import struct
####################################################
# Extraction function of temperature and date
# of a jpg FLIR i7 thermal photo
#
# Input :
# * nomphoto : Pathname to the FLIR i7 jpg
#
# Output :
# * Timage : 140x140 array of temperatures (in kelvin)
# * Date : UNIX seconds date of the jpg
#
# Inspired from https://exiftool.org/forum/index.php?action=post;topic=5602.0;last_msg=27325
# and relatives
#
####################################################
def FextraireT(nomphoto):
infile = open(nomphoto,'rb')
try:
buff = infile.read()
finally:
infile.close
def Fdecode(buffer,index,s):
# Decode with s type of buffer at index
return struct.unpack(s,buffer[index:index+struct.calcsize(s)])[0]
# APP1 FFF extract
offsetAPP1=buff.find(b'\xFF\xE1',68)
lengthAPP1=Fdecode(buff,offsetAPP1+2,'>H')
APP1=buff[offsetAPP1+12:offsetAPP1+lengthAPP1+2]
recordoffset=Fdecode(APP1,int('0x18',16),'>i')
recordnum=Fdecode(APP1,int('0x1c',16),'>i')
FRD=APP1[recordoffset:recordnum*int('0x20',16)]
# RawData (subdirectory)
posdebut=Fdecode(FRD,int('0x000',16)+int('0x0c',16),'>i')
poslongueur=Fdecode(FRD,int('0x000',16)+int('0x10',16),'>i')
RawData=APP1[posdebut:posdebut+poslongueur]
# PNG image
posinit=int('0x0020',16)
PNGimage=RawData[posinit:-11]
# It's tricky : needs to swap bytes
imagebrute=Image.open(io.BytesIO(PNGimage))
rawimg = np.array(imagebrute,dtype='int16')
rawimg.byteswap(True)
# Bytes swapped !
# CameraInfo (subdirectory)
posdebut=Fdecode(FRD,int('0x020',16)+int('0x0c',16),'>i')
poslongueur=Fdecode(FRD,int('0x020',16)+int('0x10',16),'>i')
CameraInfo=APP1[posdebut:posdebut+poslongueur]
# Emissivity E
E=Fdecode(CameraInfo,int('0x0020',16),'<f')
# Tra
Tra=Fdecode(CameraInfo,int('0x0028',16),'<f')
# PlanckR1
R1=Fdecode(CameraInfo,int('0x0058',16),'<f')
# PlanckB
B=Fdecode(CameraInfo,int('0x005c',16),'<f')
# PlanckF
F=Fdecode(CameraInfo,int('0x0060',16),'<f')
# PlanckO
O=Fdecode(CameraInfo,int('0x0308',16),'<i')
# PlanckR2
R2=Fdecode(CameraInfo,int('0x030c',16),'<f')
# date (UNIX)
Date=Fdecode(CameraInfo,int('0x0384',16),'<i')
# Temperature array
RAWrefl=R1/(R2*(np.exp(B/Tra)-F))-O
Timage=(B/np.log(R1/(R2*((rawimg-(1-E)*RAWrefl)/E+O))+F))
return Timage, Date
##############
# Example
[Timage,Date]=FextraireT('Photos/IR_0307.jpg')