only for information ;)
I found here a latest Flir FFF header definition:
Quotehttp://80.77.70.144/SwDownload/app/RssSWDownload.aspx?ID=143
FLIR GEV Demo Source Code (1.7.0)
A sample on how eBUS SDK can be used in a IR application.
This is a really extension to our old document Streaming_format_ThermoVision.pdf from 2007 with a fff.h
https://exiftool.org/forum/index.php?action=dlattach;topic=5602.0;attach=897
I reorganized the header file fff.h and added the hex addresses from our Flir Tag (record name)
Image::ExifTool::FLIR::
CameraInfo (==
FFF_TAGID_BasicData)
http://cpansearch.perl.org/src/EXIFTOOL/Image-ExifTool-10.00/lib/Image/ExifTool/FLIR.pm
The known addresses (flir.pm) I marked with "
*".
FFF_TAGID_BasicData = 0x20,
struct _bidata_t
{
GEOMETRIC_INFO_T GeometricInfo; // 32 bytes = 0x20
OBJECT_PAR_T ObjectParameters; // 48 bytes = 0x30
TEMP_CALIB_T CalibParameters; // 132 bytes = 0x84
BYTE CalibInfo[564]; // 564 bytes = 0x234
ADJUST_PAR_T AdjustParameters; // 48 bytes = 0x30
PRES_PAR_T PresentParameters; //
BYTE DisplayParameters[28];
IMAGEINFO_T ImageInfo;
DISTR_DATA_T DistributionData;
};
typedef struct _geometric_info_t
{
/** Size of one pixel in bytes.
Normal size is 2 bytes (16 bit pixels)
or 3 (for colorized YCbCr pixels) */
0x00 * unsigned short pixelSize;
0x02 unsigned short imageWidth; //!< Image width in pixels
0x04 unsigned short imageHeight; //!< Image height in pixels
/** @name Upper left coordinates
X and Y coordinates for upper left corner
relative original in case this image is a
cutout, normally 0 */
/*@{*/
0x06 unsigned short upperLeftX;
0x08 unsigned short upperLeftY;
/*@}*/
/** @name Valid pixels
The following four number identifies the
valid pixels area within the image.
Sometimes the first row and column only
contains calibration pixels that should not
be considered as real pixels */
/*@{*/
0x0A unsigned short firstValidX; //!< Normally 0
0x0C unsigned short lastValidX; //!< Normally imageWidth - 1
0x0E unsigned short firstValidY; //!< Normally 0
0x10 unsigned short lastValidY; //!< Normally imageHeight - 1
/*@}*/
0x12 unsigned short detectorDeep; //!< Number of bits from detector A/D
/** Type of detector to be able to differ
between technologies if necessary.
Defined in AppCore/core_imgflow/imgflow_state.hpp */
0x14 unsigned short detectorID;
/** Type of upsampling from Detector to IR pixels.
Defined in AppCore/core_imgflow/imgflow_state.hpp */
0x16 unsigned short upSampling;
0x18 unsigned short frameCtr; //!< Image number from frame buffer
0x1A unsigned short minMeasRadius; //!< See AppCore/core_imgflow/imgflow_state.hpp for reference
0x1C unsigned char stripeFields; //!< Number of striped fields this image consists of
0x1D unsigned char reserved; //!< For future use - should be set to 0
0x1E unsigned short reserved1; //!< For future use - should be set to 0
} GEOMETRIC_INFO_T; //!< sizeof struct == 32 bytes
struct OBJECT_PAR_T
{
0x20 * float emissivity; //!< 0 - 1
0x24 * float objectDistance; //!< Meters
0x28 * float ambTemp; //!< degrees Kelvin
0x2c * float atmTemp; /**< degrees Kelvin
- should be set == ambTemp for basic S/W */
0x30 * float extOptTemp; /**< degrees Kelvin
- should be set = ambTemp for basic S/W */
0x34 * float extOptTransm; //!< 0 - 1: should be set = 1.0 for basic S/W
0x38 float estAtmTransm; //!< 0 - 1: should be set = 0.0 for basic S/W
0x3C * float relHum; //!< relative humidity
0x40 long reserved[4]; //!< For future use - should be set to 0
}; //!< sizeof struct == 48 bytes = 0x30
struct TEMP_CALIB_T
{
0x50 long Reserved1[2]; // size 0x08
0x58 * float R; //!< Calibration constant R
0x5C * float B; //!< Calibration constant B
0x60 * float F; //!< Calibration constant F
0x64 long Reserved2[11]; // size 44 = 0x2C reserved for AtmosphericTrans !!!
0x90 * float tmax; //!< Upper temp limit [K] when calibrated for
//!< current temp range
0x94 * float tmin; //!< Lower temp limit [K] when calibrated for
//!< current temp range
0x98 * float tmaxClip; //!< Upper temp limit [K] over which the
//!< calibration becomes invalid
0x9c * float tminClip; //!< Lower temp limit [K] under which the
//!< calibration becomes invalid
0xA0 * float tmaxWarn; //!< Upper temp limit [K] over which the
//!< calibration soon will become invalid
0xA4 * float tminWarn; //!< Lower temp limit [K] under which the
//!< calibration soon will become invalid
0xA8 * float tmaxSaturated; //!< Upper temp limit [K] over which pixels
//!< should be presented with overflow color
0xAC * float tminSaturated; //!< Lower temp limit [K] for saturation
//!< (see also ADJUST_PAR_T:ipixOverflow).
//!< ipix over/under flow should be calculated
//!< by imgsrc from tmin/maxSaturated.
//!< LUT handler should look at ipix
//!< over/underflow.
0xB0 long Reserved3[9]; // size 36 = 0x24
}; //!< sizeof struct == 132 bytes = 0x84
struct CalibInfo
{
0xD4 * BYTE CalibInfo[564]; // 564 bytes = 0x234
} CalibInfo; //!< sizeof struct == 132 bytes = 0x84
typedef struct _adjust_par_t {
0x308 * long normOffset; /* Temperature compensation offset !!!! PlanckO !!!!
(globalOffset) */
0x30C * float normGain; /* Temperature compensation gain !!!! PlanckR2 !!!!
(globalGain) */
0x30E unsigned short ipixUnderflow; /* Image pixel underflow limit */
0x310 unsigned short ipixOverflow; /* Image pixel overflow limit */
0x314 long Reserved2[9];
} ADJUST_PAR_T; /* sizeof struct == 48 bytes = 0x30 */
typedef struct _pres_par_t {
0x338 * signed long level; /* Level as normalized pixel value (apix), Level is defined as middle of
span (in pixel units) */
0x33C * signed long span; /* Span as normalized pixel value (apix) */
0x340 BYTE reserved[40]; // 0x28
} PRES_PAR_T; /* sizeof struct == 48 bytes = 0x30 */
typedef struct DisplayParameters {
0x368 BYTE DisplayParameters[28];
} DisplayParameters; /* sizeof struct == 28 bytes = 0x1C */
struct IMAGEINFO_T
{
0x384 * unsigned long imageTime; //!< Time in seconds since 1970-01-01 00:00 (UTC)
0x388 unsigned long imageMilliTime; //!< Milliseconds since last second
0x38C short timeZoneBias; //!< Time zone bias in minutes
//! UTC = local time + bias
0x38E short swReserved1; //!< filler == 0
0x390 * long focusPos; //!< Focus position as counter value
0x394 float fTSTemp[7]; // 0x1C !< Temp sensor values converted to Kelvin
0x3B0 float fTSTempExt[4]; // 0x10 !< Lens temp sensors et.c. Converted to Kelvin
0x3C0 unsigned short trigInfoType; //!< 0 = No info, 1 = THV 900 type info
0x3C2 unsigned short trigHit; //!< hit count - microsecs from trig
//! reference
0x3C4 unsigned short trigFlags; //!< trig flags, type dependent
0x3C6 unsigned short reserved1;
0x3C8 unsigned long trigCount; //!< trig counter
0x3CB short manipulType; //!< defines how to interpret manipFactors
0x3CE short manipFactors[5]; //!< Used average factors
/** Detecor settings - camera type dependent */
0x3D8 long detectorPars[20]; //!< Currently used detector parameters like
//! used bias, offsets. Usage is camera
//! dependent
0x428 long reserved[5]; //!< For future use
}; //!< sizeof struct == 184 bytes = 0xB8
struct DISTR_DATA_T
{
/** Framegrab independent distribution data */
0x43C char imgName[16]; /* (4 longs) */
0x44C unsigned short distrLive; //!< TRUE (1) when image distribution is
//! 'LIVE'. FALSE (0) otherwise
0x44E unsigned short distrRecalled; //!< TRUE (1) when image distribution is
//! recalled. FALSE (0) otherwise.
//!< TRUE also implies that distrLive ==
//! FALSE
0x450 long curGlobalOffset;
0x454 float curGlobalGain; //!< globalOffset/Gain to generate LUT from
//! updated continously when live only
#define regulationOn 1
0x458 unsigned short regulMethodMask; //!< Method used for o/g calculation
0x45A unsigned short visualImage; //!< TRUE (1) for TV (visual)
//! FALSE (0) for IR image
0x45C * float focusDistance; //!< focusDistance in meters.
//! 0 means not defined.
//! NOT calculated by image source
0x460 unsigned short StripeHeight; //!< 0 = not striped
0x462 unsigned short StripeStart; //!< Striping start line if striped
0x464 * unsigned short imageFreq; //!< Image frequency, defines the nominal
//! image frequency in Hz
0x466 unsigned short typePixStreamCtrlData;
//!< 0 = no such data,
//! other types TBD
0x468 unsigned short PixStreamDataLine;
//!< At which line to find
//! PixStreamCtrlData if any
#define IMGSMSK_NUC 0x20 //!< Bit set means that NUC is in progress
0x46A short errStatus; //!< bit field, mask definitions above
0x46C unsigned short imageMilliFreq; //!< Image frequency, milliHz part of imageFreq
0x46E short reserved; //!< For future use
0x470 long reserved2[3];
}; //!< sizeof struct == 64 bytes = 0x40
I marked
0x464 * unsigned short imageFreq; //!< Image frequency, defines the nominal
because I rechecked some times ago in eevblog forum, that the frame rate is 8 Byte behind FocusDistance.
With this knowledge I helped Encryptededdy to wrote a script thats convert raw thermal images from a non Flir thermal camera to a Flir radiometric video. Then you can open the radiometric video with Flir Tools :D
see the nice sample with a great flir thermal video from a opgal thermal camera:
http://www.eevblog.com/forum/testgear/opgal-therm-app-first-smartphone-thermal-imager-to-ship/msg764924/#msg764924
Furthermore you see, that in this current header ( fff.h dated to 2013) the "Atmospheric Values" part is marked with reserved
0x64 long Reserved2[11]; // size 44 = 0x2C reserved
without the help from jskala I never hacked this really hard formula
https://exiftool.org/forum/index.php/topic,4898.msg27530.html#msg27530
embedded in Flir.pm
0x070 => { Name => 'AtmosphericTransAlpha1', %float6f }, #1 (value: 0.006569)
0x074 => { Name => 'AtmosphericTransAlpha2', %float6f }, #1 (value: 0.012620)
0x078 => { Name => 'AtmosphericTransBeta1', %float6f }, #1 (value: -0.002276)
0x07C => { Name => 'AtmosphericTransBeta2', %float6f }, #1 (value: -0.006670)
0x080 => { Name => 'AtmosphericTransX', %float6f }, #1 (value: 1.900000)
PS: the flir tags (you say record names) in fff.h :
typedef enum {
/* General tags */
FFF_TAGID_FREE = 0, /* Marks unused tag descriptor */
FFF_TAGID_Pixels = 1,
FFF_TAGID_GainMap = 2,
FFF_TAGID_OffsMap = 3,
FFF_TAGID_DeadMap = 4,
FFF_TAGID_GainDeadMap = 5,
FFF_TAGID_CoarseMap = 6,
FFF_TAGID_ImageMap = 7,
FFF_TAGID_SubFlirFileHead = 0x1e,
FFF_general_high = 0x1f, /* Reserve space for other general
tags */
/* FLIR TAGs */
FFF_TAGID_BasicData = 0x20,
FFF_TAGID_Measure,
FFF_TAGID_ColorPal,
FFF_TAGID_TextComment,
FFF_TAGID_VoiceComment,
FFF_TAGID_matrix_high = 0x3f, /* reserve space for other system
image blocks */
/* FLIR Boston reserved TAG number series */
FFF_TAGID_Boston_reserved = 0x40,
FFF_TAGID_Boston_reserved_high = 0x5f,
FFF_highnum = 0x100 /* Guarantee 2 bytes enum */
} TAG_MAIN_T;
in the firmware update file from a Flir E40 I found this collection of the FLIR Record Names
$ strings flir\ e40/flir_exx_pn490_v2.23.14_update_pack/E2comb_v2.23.14.fif/FlashFS/system/ftest.exe | grep -i FFF_TAGID
FFF_TAGID_FREE
FFF_TAGID_Pixels
FFF_TAGID_GainMap
FFF_TAGID_OffsMap
FFF_TAGID_DeadMap
FFF_TAGID_GainDeadMap
FFF_TAGID_CoarseMap
FFF_TAGID_ImageMap
FFF_TAGID_RRMap
FFF_TAGID_OOMap
FFF_TAGID_OPMap
FFF_TAGID_BackgrMap
FFF_TAGID_ShutterMap
FFF_TAGID_MMap
FFF_TAGID_YCbCrPixels
FFF_TAGID_FloatOffsMap
FFF_TAGID_DiffPixels
FFF_TAGID_GasOverlay
FFF_TAGID_CompressedBurst
FFF_TAGID_DistanceMap
FFF_TAGID_BasicData
FFF_TAGID_Measure
FFF_TAGID_ColorPal
FFF_TAGID_TextComment
FFF_TAGID_VoiceComment
FFF_TAGID_MeasureResult
FFF_TAGID_VisualMarker
FFF_TAGID_IRMarker
FFF_TAGID_IRImageOverlay
FFF_TAGID_Alarm
FFF_TAGID_Fusion
FFF_TAGID_GPS
FFF_TAGID_ExternalSensors
FFF_TAGID_ImageID
FFF_TAGID_VisibleParams
FFF_TAGID_VisualImageOverlay
FFF_TAGID_Incompatible_BasicData_1
No feature request!
I got here a DeadMap file from an old Flir i5
http://www.eevblog.com/forum/testgear/flir-e4-thermal-imaging-camera-teardown/msg777818/#msg777818
the ds250C_we_ap_fi_le.gan has the Flir Tag FFF_TAGID_DeadMap = 4
(broken link ref3 in flir.pm as attachment or see fff.h from first post)
with the config.txt file from attachment we can extract the RAW 80x80 image
>exiftool -config config.txt ds250C_we_ap_fi_le.gan
ExifTool Version Number : 9.91
File Name : ds250C_we_ap_fi_le.gan
File Size : 13 kB
...
File Type : FLIR
MIME Type : application/unknown
Creator Software :
Embedded Image Width : 80
Embedded Image Height : 80
Dead Map : (Binary data 12800 bytes, use -b option to extract)
>exiftool -config config.txt -b -DeadMap ds250C_we_ap_fi_le.gan > 1.hex
now convert with IM resize for better bad pixel peeping
>exiftool -config config.txt -b -DeadMap ds250C_we_ap_fi_le.gan > 1.hex
>convert -depth 16 -endian lsb -size 80x80 gray:1.hex t2.png
>convert t2.png -filter point -resize 320x resize.png
No feature request!
with knowledge from first post and flir.pm a comparison of the Tag names:
http://cpansearch.perl.org/src/EXIFTOOL/Image-ExifTool-10.00/lib/Image/ExifTool/FLIR.pm
TAGID | Exiftool_Tag_Name | Flir_Tag_Name |
00 | free | FFF_TAGID_FREE |
01 | | FFF_TAGID_Pixels |
02 | GainMap | FFF_TAGID_GainMap |
03 | OffsMap | FFF_TAGID_OffsMap |
04 | DeadMap | FFF_TAGID_DeadMap |
05 | GainDeadData | FFF_TAGID_GainDeadMap |
06 | CoarseData | FFF_TAGID_CoarseMap |
07 | ImageMap | FFF_TAGID_ImageMap |
08 | | FFF_TAGID_RRMap |
09 | | FFF_TAGID_OOMap |
0A | | FFF_TAGID_OPMap |
0B | | FFF_TAGID_BackgrMap |
0C | | FFF_TAGID_ShutterMap |
0D | | FFF_TAGID_MMap |
0E | EmbeddedImage | FFF_TAGID_YCbCrPixels |
0F ?? | | FFF_TAGID_FloatOffsMap |
10 ?? | | FFF_TAGID_DiffPixels |
11 ?? | | FFF_TAGID_GasOverlay |
12 ?? | | FFF_TAGID_CompressedBurst |
13 ?? | | FFF_TAGID_DistanceMap |
------ |
1E | | FFF_TAGID_SubFlirFileHead |
1F | | FFF_general_high = 0x1f, /* Reserve space for other general tags |
20 | CameraInfo | FFF_TAGID_BasicData |
21 | MeasurementInfo | FFF_TAGID_Measure |
22 | PaletteInfo | FFF_TAGID_ColorPal |
23 | TextInfo | FFF_TAGID_TextComment |
24 | EmbeddedAudioFile | FFF_TAGID_VoiceComment |
25 | | FFF_TAGID_MeasureResult |
26 | | FFF_TAGID_VisualMarker |
27 | | FFF_TAGID_IRMarker |
28 | PaintData | FFF_TAGID_IRImageOverlay |
29 | | FFF_TAGID_Alarm |
2A | PiP | FFF_TAGID_Fusion |
2B | GPSInfo | FFF_TAGID_GPS |
2C | MeterLink | FFF_TAGID_ExternalSensors |
2D | | FFF_TAGID_ImageID |
2E | ParameterInfo | FFF_TAGID_VisibleParams |
2F ?? | | FFF_TAGID_VisualImageOverlay |
30 ?? | | FFF_TAGID_Incompatible_BasicData_1 |
------ |
3F | | FFF_TAGID_matrix_high = 0x3f, /* reserve space for other system image blocks */ |
40 | | FFF_TAGID_Boston_reserved = 0x40, |
5F | | FFF_TAGID_Boston_reserved_high = 0x5f, |
unsure numbers marked with "??"
Hi Tomas,
It is great that you are documenting this here! Thanks!
For reference, here is a link to the main FLIR thread (https://exiftool.org/forum/index.php/topic,4898.0.html)
I may move the current thread to the Metadata section of the forum.
- Phil
Hi Phil,
I have much fun to answer the questions about "exiftool for Flir cameras" in the forum
http://www.eevblog.com/forum/testgear/
There are many special Flir tags, which not worth for you to take this in your Exiftool code.
As a sample the Flir Ultramax feature.
http://blog.ivytools.com/2014/10/30/seeing-double-flir-ultramax-image-enhancement/
QuoteFLIR UltraMax captures 16 thermal images in less than one second.
..the user can "Enhance image resolution (UltraMax)...
The enhanced image will have twice the original resolution and four times as many pixels.
All pixels still include radiometric data, just as with normal FLIR thermal images.
it's not magic but a simple multiframe superresolution of images with minor movements
I got such a radiometric jpg sample from a Flir T440 with a embedded sequence of 16 images (sensor 320x240).
Flir created a new TAG 0x15 to save this 16 raw images.
In this config.txt file I used the name "CompressedBurst" from the list above:
# The %Image::ExifTool::UserDefined hash defines new tags to be added to existing tables.
%Image::ExifTool::UserDefined = (
'Image::ExifTool::FLIR::FFF' => {
0x15 => {
Name => 'CompressedBurst',
SubDirectory => { TagTable => 'Image::ExifTool::FLIR::CompressedBurst' },
},
},
);
# define new table
%Image::ExifTool::FLIR::CompressedBurst = (
GROUPS => { 0 => 'APP1', 2 => 'Image' },
PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
FIRST_ENTRY => 0,
NOTES => q{
16 RAW frames of CompressedBurst - 1 second
},
0x00 => {
# use this tag only to determine the byte order
# (the value should be 0x0003 if the byte order is correct)
Name => 'CameraInfoByteOrder',
Format => 'int16u',
Hidden => 1,
RawConv => 'ToggleByteOrder() if $val >= 0x0100; undef',
},
0x02 => {
Name => 'EmbeddedImageWidth',
Format => 'int16u',
},
0x04 => {
Name => 'EmbeddedImageHeight',
Format => 'int16u',
},
0x20 => {
Name => 'CompressedBurst',
Format => 'undef[$size-0x20]',
Notes => 'unknown format',
Binary => 1,
},
);
It's very tricky: every image from the 16 images is stored in an own TAG 0x15.
$ exiftool -config config.txt -v T440.jpg | grep "FLIR Record 0x"
| | FLIR Record 0x01, offset 0x0420, length 0x1a7db
| | FLIR Record 0x20, offset 0x1abfc, length 0x09ac
| | FLIR Record 0x22, offset 0x1b5a8, length 0x0310
| | FLIR Record 0x21, offset 0x1b8b8, length 0x0034
| | FLIR Record 0x2a, offset 0x1b8ec, length 0x0060
| | FLIR Record 0x0e, offset 0x1b94c, length 0x666b9
| | FLIR Record 0x29, offset 0x82008, length 0x0008
| | FLIR Record 0x15, offset 0x82010, length 0x88a0
| | FLIR Record 0x15, offset 0x8a8b0, length 0x88e8
| | FLIR Record 0x15, offset 0x93198, length 0x8908
| | FLIR Record 0x15, offset 0x9baa0, length 0x88f0
| | FLIR Record 0x15, offset 0xa4390, length 0x88a8
| | FLIR Record 0x15, offset 0xacc38, length 0x88b8
| | FLIR Record 0x15, offset 0xb54f0, length 0x88c0
| | FLIR Record 0x15, offset 0xbddb0, length 0x88d0
| | FLIR Record 0x15, offset 0xc6680, length 0x88e8
| | FLIR Record 0x15, offset 0xcef68, length 0x88e8
| | FLIR Record 0x15, offset 0xd7850, length 0x88c0
| | FLIR Record 0x15, offset 0xe0110, length 0x8888
| | FLIR Record 0x15, offset 0xe8998, length 0x8870
| | FLIR Record 0x15, offset 0xf1208, length 0x88a8
| | FLIR Record 0x15, offset 0xf9ab0, length 0x88a0
| | FLIR Record 0x15, offset 0x102350, length 0x8898
But after reading your manpage I found a nice switch to export all images with exiftool
"-a Allow duplicate tags to be extracted": :)
Great!!
$ exiftool -config config.txt -a -b -CompressedBurst -v -W "Image/%.2c.hex" T440.jpg
Warning: GPS pointer references previous ExifIFD directory - T440.jpg
Created directory Image
Wrote CompressedBurst to Image/00.hex
Wrote CompressedBurst to Image/01.hex
Wrote CompressedBurst to Image/02.hex
Wrote CompressedBurst to Image/03.hex
Wrote CompressedBurst to Image/04.hex
Wrote CompressedBurst to Image/05.hex
Wrote CompressedBurst to Image/06.hex
Wrote CompressedBurst to Image/07.hex
Wrote CompressedBurst to Image/08.hex
Wrote CompressedBurst to Image/09.hex
Wrote CompressedBurst to Image/10.hex
Wrote CompressedBurst to Image/11.hex
Wrote CompressedBurst to Image/12.hex
Wrote CompressedBurst to Image/13.hex
Wrote CompressedBurst to Image/14.hex
Wrote CompressedBurst to Image/15.hex
1 directories created
16 output files created
And now comes the next trouble.
The magic bytes of the images are "ff d8 ff f7":
$ hexdump -C -n 32 Image/00.hex
00000000 ff d8 ff f7 00 0b 10 00 f0 01 40 01 01 11 00 ff |..........@.....|
00000010 f8 00 0d 01 ff ff 00 30 00 75 01 5a 00 40 ff da |.......0.u.Z.@..| This is the lossless jpg format JPG-LS
http://www.digitalpreservation.gov/formats/fdd/fdd000151.shtml
Exiftool can't read it :o
$ exiftool ./Image/01.hex
ExifTool Version Number : 10.02
File Name : 01.hex
Directory : ./Image
File Size : 34 kB
...
File Type : JPEG
File Type Extension : jpg
MIME Type : image/jpeg
After googling around I found, that ffmpeg can read and write this really
exceptionally format.
$ ffmpeg -codecs | grep jpegls
ffmpeg version 2.7.2 Copyright (c) 2000-2015 the FFmpeg developers
DEVILS jpegls JPEG-LS
And ffmpeg has also a nice sequence syntax like exiftool to convert 16 images in one line:
$ ffmpeg -f image2 -vcodec jpegls -i "./Image/%02d.hex" -f image2 -vcodec png burst%02d.png
the png results are readable with exiftool:
$ exiftool burst01.png
ExifTool Version Number : 10.02
File Size : 120 kB
...
File Type : PNG
File Type Extension : png
MIME Type : image/png
Image Width : 320
Image Height : 240
Bit Depth : 16
Color Type : Grayscale
Compression : Deflate/Inflate
Filter : Adaptive
Interlace : Noninterlaced
Pixels Per Unit X : 0
Pixels Per Unit Y : 1
Pixel Units : Unknown
Image Size : 320x240
Megapixels : 0.077
a lot of fun ;)
Very interesting. Could you post an example of a JPEG-LS image?
Thanks.
- Phil
I'm happy to help you:
$ wget http://www.imagemagick.org/Usage/fourier/lena.png
$ ffmpeg -f image2 -vcodec png -i lena.png -f image2 -vcodec jpegls lena.jpg
$ identify lena.jpg
identify: Unsupported marker type 0xf7 `lena.jpg' @ error/jpeg.c/JPEGErrorHandler/321.
$ hexdump -C -n 32 lena.jpg
00000000 ff d8 ff f7 00 11 08 00 80 00 80 03 01 11 00 02 |................|
00000010 11 00 03 11 00 ff da 00 0c 03 01 00 02 00 03 00 |................|
$ ffmpeg -f image2 -vcodec jpegls -i lena.jpg -f image2 -vcodec png lena2.png
I saw ".jls" as extension for JPEG-LS.
Flirs new politic of disclosure:
I found here a fff.h header with date of 2. November 2015
FLIR GEV Demo Source Code (1.8.0)
http://80.77.70.144/SwDownload/app/RssSWDownload.aspx?ID=143
The major extension compared to first post is the announcement of all calibration and atmospheric values:
(hex adresses from http://cpansearch.perl.org/src/EXIFTOOL/Image-ExifTool-10.00/lib/Image/ExifTool/FLIR.pm )
0x058 float R; //!< Calibration constant R
float B; //!< Calibration constant B
float F; //!< Calibration constant F
long Reserved2[3];
0x070 float alpha1; /* Attenuation for atmosphere without water vapor. */
float alpha2; /* Attenuation for atmosphere without water vapor */
float beta1; /* Attenuation for water vapor */
float beta2; /* Attenuation for water vapor */
float X; /* Scaling factor for attenuation. */
.....
0x308 long normOffset; /* Temperature compensation offset !!!! PlanckO !!!!
(globalOffset) */
0x30C float normGain; /* Temperature compensation gain !!!! PlanckR2 !!!!
(globalGain) */
and three new structs
/** Temp sensor data struct */
struct TEMPSENSOR_DATA_T
{
float fTSTemp; //!< Converted to Kelvin
char pzTSName[SYSIMG_NAMELEN+1];
ULONG captureTime; //!< TS updated; time in seconds since 1970-01-01 00:00
ULONG captureMilliTime; //!< TS updated; Milliseconds since last second
}; //!< sizeof struct == 28 bytes
/** Detector parameter struct */
struct DETECTOR_PARAM_T
{
float fData;
char pzDPName[SYSIMG_NAMELEN+1];
}; //!< sizeof struct == 20 bytes
/**
Extended image info (more tempsensor and detector data)
*/
struct EXTENDED_IMAGEINFO_T
{
TEMPSENSOR_DATA_T tsData[20]; //!< Temp sensor data
DETECTOR_PARAM_T detectorPars[20]; //!< Currently used detector parameters like
//! used bias, offsets. Usage is camera
//! dependent
}; //!< sizeof struct == 960 bytes
in PvSimpleUISampleDlg.cpp there are the Flir formulas
USHORT PvSimpleUISampleDlg::TauToSignal(float C)
{
USHORT s;
float K = C + 273.15f;
s = (USHORT)(m_R /(exp(m_B/K) - m_F) + m_O);
return s;
}
float PvSimpleUISampleDlg::TauToTemp(USHORT sig)
{
float T;
double objSig;
objSig = powToObjSig((double)sig);
T = (float)(m_B / log(m_R /(objSig - m_O) + m_F));
return T;
}
double PvSimpleUISampleDlg::doCalcAtmTao(void)
{
double tao, dtao;
double H, T, sqrtD, X, a1, b1, a2, b2;
double sqrtH2O;
double TT;
double a1b1sqH2O, a2b2sqH2O, exp1, exp2;
CTemperature C(CTemperature::Celsius);
#define H2O_K1 +1.5587e+0
#define H2O_K2 +6.9390e-2
#define H2O_K3 -2.7816e-4
#define H2O_K4 +6.8455e-7
#define TAO_TATM_MIN -30.0
#define TAO_TATM_MAX 90.0
#define TAO_SQRTH2OMAX 6.2365
#define TAO_COMP_MIN 0.400
#define TAO_COMP_MAX 1.000
H = m_RelHum;
C = m_AtmTemp;
T = C.Value(); // We need Celsius to use constants defined above
sqrtD = sqrt(m_ObjectDistance);
X = m_X;
a1 = m_alpha1;
b1 = m_beta1;
a2 = m_alpha2;
b2 = m_beta2;
if (T < TAO_TATM_MIN)
T = TAO_TATM_MIN;
else if (T > TAO_TATM_MAX)
T = TAO_TATM_MAX;
TT = T*T;
sqrtH2O = sqrt(H*exp(H2O_K1 + H2O_K2*T + H2O_K3*TT + H2O_K4*TT*T));
if ( sqrtH2O > TAO_SQRTH2OMAX )
sqrtH2O = TAO_SQRTH2OMAX;
a1b1sqH2O = (a1+b1*sqrtH2O);
a2b2sqH2O = (a2+b2*sqrtH2O);
exp1 = exp(-sqrtD*a1b1sqH2O);
exp2 = exp(-sqrtD*a2b2sqH2O);
tao = X*exp1 + (1-X)*exp2;
dtao = -(a1b1sqH2O*X*exp1+a2b2sqH2O*(1-X)*exp2);
// The real D-derivative is also divided by 2 and sqrtD.
// Here we only want the sign of the slope! */
if (tao < TAO_COMP_MIN)
tao = TAO_COMP_MIN; // below min value, clip
else if (tao > TAO_COMP_MAX)
{
// check tao at 1 000 000 m dist
tao = X*exp(-(1.0E3)*a1b1sqH2O)+(1.0-X)*exp(-(1.0E3)*a2b2sqH2O);
if ( tao > 1.0 ) // above max, staying up, assume \/-shape
tao = TAO_COMP_MIN;
else
tao = TAO_COMP_MAX; // above max, going down, assume /\-shape
}
else if ( dtao > 0.0 && m_ObjectDistance > 0.0)
tao = TAO_COMP_MIN; // beween max & min, going up, assume \/
// else between max & min, going down => OK as it is, ;-)
return( tao);
}