News:

2023-03-15 Major improvements to the new Geolocation feature

Main Menu

Flir FFF Header

Started by tomas123, October 08, 2015, 01:10:26 PM

Previous topic - Next topic

tomas123

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

tomas123

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

tomas123

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


TAGIDExiftool_Tag_NameFlir_Tag_Name
00free FFF_TAGID_FREE
01FFF_TAGID_Pixels
02GainMap FFF_TAGID_GainMap
03OffsMap FFF_TAGID_OffsMap
04DeadMap FFF_TAGID_DeadMap
05GainDeadDataFFF_TAGID_GainDeadMap
06CoarseDataFFF_TAGID_CoarseMap
07ImageMap FFF_TAGID_ImageMap
08FFF_TAGID_RRMap
09FFF_TAGID_OOMap
0AFFF_TAGID_OPMap
0BFFF_TAGID_BackgrMap
0CFFF_TAGID_ShutterMap
0DFFF_TAGID_MMap
0EEmbeddedImageFFF_TAGID_YCbCrPixels
0F ??FFF_TAGID_FloatOffsMap
10 ??FFF_TAGID_DiffPixels
11 ??FFF_TAGID_GasOverlay
12 ??FFF_TAGID_CompressedBurst
13 ??FFF_TAGID_DistanceMap
------
1EFFF_TAGID_SubFlirFileHead
1FFFF_general_high = 0x1f,   /* Reserve space for other general tags
20CameraInfoFFF_TAGID_BasicData
21MeasurementInfoFFF_TAGID_Measure
22PaletteInfoFFF_TAGID_ColorPal
23TextInfoFFF_TAGID_TextComment
24EmbeddedAudioFileFFF_TAGID_VoiceComment
25FFF_TAGID_MeasureResult
26FFF_TAGID_VisualMarker
27FFF_TAGID_IRMarker
28PaintDataFFF_TAGID_IRImageOverlay
29FFF_TAGID_Alarm
2APiPFFF_TAGID_Fusion
2BGPSInfoFFF_TAGID_GPS
2CMeterLinkFFF_TAGID_ExternalSensors
2DFFF_TAGID_ImageID
2EParameterInfoFFF_TAGID_VisibleParams
2F ??FFF_TAGID_VisualImageOverlay
30 ??FFF_TAGID_Incompatible_BasicData_1
------
3FFFF_TAGID_matrix_high = 0x3f,       /* reserve space for other system  image blocks */
40FFF_TAGID_Boston_reserved = 0x40,
5FFFF_TAGID_Boston_reserved_high = 0x5f,

unsure numbers marked with "??"

Phil Harvey

Hi Tomas,

It is great that you are documenting this here!  Thanks!

For reference, here is a link to the main FLIR thread

I may move the current thread to the Metadata section of the forum.

- Phil
...where DIR is the name of a directory/folder containing the images.  On Mac/Linux/PowerShell, use single quotes (') instead of double quotes (") around arguments containing a dollar sign ($).

tomas123

#4
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  ;)

Phil Harvey

Very interesting.  Could you post an example of a JPEG-LS image?

Thanks.

- Phil
...where DIR is the name of a directory/folder containing the images.  On Mac/Linux/PowerShell, use single quotes (') instead of double quotes (") around arguments containing a dollar sign ($).

tomas123

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.


tomas123

#7
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);
}