metadata-extractor icon indicating copy to clipboard operation
metadata-extractor copied to clipboard

FLIR camera support

Open mauricebaruk opened this issue 7 years ago • 13 comments

Hello, I was wondering if there is any plan to support FLIR cameras ? http://www.flir.com/uploadedFiles/sUAS/Products/Vue-Pro/FLIR-Interface-Requirements-TIFF.pdf

On my side, I was trying to access Date/Time Original with metadata-extractor, no value. However ExifTool is able to find it

mauricebaruk avatar Sep 28 '16 15:09 mauricebaruk

Skimming the document, it seems to be regular TIFF, which is supported. Have you tried it?

Can you provide a sample image from the camera? (read this)

drewnoakes avatar Sep 28 '16 20:09 drewnoakes

I tried it, and it partially work. The directory have some values, but not the "Date/Time Original", what I need.

mauricebaruk avatar Sep 28 '16 21:09 mauricebaruk

Here is a photo flir

ExifTool output:

     - ExifTool Version Number         : 10.28
     - File Name                       : FLIR.jpg
     - Directory                       : .
     - File Size                       : 685 kB
     - File Modification Date/Time     : 2016:09:28 17:19:35-04:00
     - File Access Date/Time           : 2016:09:28 17:25:14-04:00
     - File Creation Date/Time         : 2016:09:28 17:25:14-04:00
     - File Permissions                : rw-rw-rw-
     - File Type                       : JPEG
     - File Type Extension             : jpg
     - MIME Type                       : image/jpeg
0x0000 JFIF Version                    : 1.02
     - Exif Byte Order                 : Little-endian (Intel, II)
0x010f Make                            : FLIR
0x011a X Resolution                    : 72
0x011b Y Resolution                    : 72
0x0128 Resolution Unit                 : inches
0x0213 Y Cb Cr Positioning             : Centered
0x9000 Exif Version                    : 0210
0x9101 Components Configuration        : Y, Cb, Cr, -
0x9214 Subject Area                    : 320 256 640 512
0xa000 Flashpix Version                : 0100
0xa001 Color Space                     : sRGB
0xa002 Exif Image Width                : 640
0xa003 Exif Image Height               : 512
0x0000 GPS Version ID                  : 2.2.0.0
0x0001 GPS Latitude Ref                : South
0x0003 GPS Longitude Ref               : West
0x0005 GPS Altitude Ref                : Above Sea Level
0x0007 GPS Time Stamp                  : 15:29:41
0x000c GPS Speed Ref                   : km/h
0x000d GPS Speed                       : 0
0x000e GPS Track Ref                   : True North
0x000f GPS Track                       : 0
0x0004 Creator Software                : FLIR
0x0001 Raw Thermal Image Width         : 640
0x0002 Raw Thermal Image Height        : 512
0x0010 Raw Thermal Image Type          : TIFF
0x0010 Raw Thermal Image               : (Binary data 655564 bytes, use -b option to extract)
0x0020 Emissivity                      : 1.00
0x0024 Object Distance                 : 1.00 m
0x0028 Reflected Apparent Temperature  : 22.0 C
0x002c Atmospheric Temperature         : 22.0 C
0x0030 IR Window Temperature           : 22.0 C
0x0034 IR Window Transmission          : 1.00
0x003c Relative Humidity               : 30.0 %
0x0058 Planck R1                       : 366545
0x005c Planck B                        : 1428
0x0060 Planck F                        : 1
0x0070 Atmospheric Trans Alpha 1       : 0.006569
0x0074 Atmospheric Trans Alpha 2       : 0.012620
0x0078 Atmospheric Trans Beta 1        : -0.002276
0x007c Atmospheric Trans Beta 2        : -0.006670
0x0080 Atmospheric Trans X             : 1.900000
0x0090 Camera Temperature Range Max    : 135.0 C
0x0094 Camera Temperature Range Min    : -25.0 C
0x00d4 Camera Model                    : Vue Pro 640 13mm
0x00f4 Camera Part Number              : 436-0017-00
0x0104 Camera Serial Number            : 162682
0x0114 Camera Software                 : 22.20.16.1
0x0170 Lens Model                      : FOL13
0x0190 Lens Part Number                : T197089
0x01a0 Lens Serial Number              : 1175627
0x01b4 Field Of View                   : 45.0 deg
0x01ec Filter Model                    : FOL13
0x01fc Filter Part Number              : 
0x021c Filter Serial Number            : 1175627
0x0308 Planck O                        : 342
0x030c Planck R2                       : 1
0x0338 Raw Value Median                : 8137
0x033c Raw Value Range                 : 345
0x0384 Date/Time Original              : 2016:06:27 15:29:42.041-04:00
0x0390 Focus Step Count                : 0
0x045c Focus Distance                  : 0.0 m
0x0058 GPS Map Datum                   : 
     - Image Width                     : 640
     - Image Height                    : 512
     - Encoding Process                : Baseline DCT, Huffman coding
     - Bits Per Sample                 : 8
     - Color Components                : 3
     - Y Cb Cr Sub Sampling            : YCbCr4:2:0 (2 2)
     - GPS Altitude                    : 0 m Above Sea Level
     - GPS Latitude                    : 0 deg 0' 0.00" N
     - GPS Longitude                   : 0 deg 0' 0.00" E
     - GPS Position                    : 0 deg 0' 0.00" N, 0 deg 0' 0.00" E
     - Image Size                      : 640x512
     - Megapixels                      : 0.328
     - Peak Spectral Sensitivity       : 10.1 um

metadata-extractor output:

[JPEG] Compression Type - Baseline
[JPEG] Data Precision - 8 bits
[JPEG] Image Height - 512 pixels
[JPEG] Image Width - 640 pixels
[JPEG] Number of Components - 3
[JPEG] Component 1 - Y component: Quantization table 0, Sampling factors 2 horiz/2 vert
[JPEG] Component 2 - Cb component: Quantization table 1, Sampling factors 1 horiz/1 vert
[JPEG] Component 3 - Cr component: Quantization table 1, Sampling factors 1 horiz/1 vert
[JFIF] Version - 1.2
[JFIF] Resolution Units - inch
[JFIF] X Resolution - 96 dots
[JFIF] Y Resolution - 96 dots
[JFIF] Thumbnail Width Pixels - 0
[JFIF] Thumbnail Height Pixels - 0
[Exif IFD0] Make - FLIR
[Exif IFD0] X Resolution - 72 dots per inch
[Exif IFD0] Y Resolution - 72 dots per inch
[Exif IFD0] Resolution Unit - Inch
[Exif IFD0] YCbCr Positioning - Center of pixel array
[Exif SubIFD] Exif Version - 2.10
[Exif SubIFD] Components Configuration - YCbCr
[Exif SubIFD] Subject Location - 320 256 640 512
[Exif SubIFD] FlashPix Version - 1.00
[Exif SubIFD] Color Space - sRGB
[Exif SubIFD] Exif Image Width - 640 pixels
[Exif SubIFD] Exif Image Height - 512 pixels
[GPS] GPS Version ID - 2.200
[GPS] GPS Latitude Ref - S
[GPS] GPS Latitude - 0° 0' 0"
[GPS] GPS Longitude Ref - W
[GPS] GPS Longitude - 0° 0' 0"
[GPS] GPS Altitude Ref - Sea level
[GPS] GPS Altitude - 0 metres
[GPS] GPS Time-Stamp - 15:29:41,000 UTC
[GPS] GPS Speed Ref - kph
[GPS] GPS Speed - 0
[GPS] GPS Track Ref - True direction
[GPS] GPS Track - 0 degrees
[File] File Name - FLIR.jpg
[File] File Size - 701803 bytes
[File] File Modified Date - mer. sept. 28 17:20:09 -04:00 2016

mauricebaruk avatar Sep 28 '16 21:09 mauricebaruk

Additionally, I was able to convert the binary data to unix timestamp once I found it. However I'm really uncomfortable to contribute to the library itself.

        RandomAccessFile randomAccessFile = new RandomAccessFile(flirPhoto, "r");
        RandomAccessFileReader reader = new RandomAccessFileReader(randomAccessFile);

        int dateOffset = 0xA0E70;
        long timestamp = reader.getUInt32(dateOffset);
        long milliseconds = reader.getUInt32(dateOffset + 4) & 0xFFFF;
        long timezone = reader.getInt16(dateOffset + 8); // Timezone in minutes
        System.out.println( timestamp - timezone * 60); // Unix timestamp adjusted with timezone
        System.out.println(milliseconds); // milliseconds

I used ExifTool source code to make this.

mauricebaruk avatar Sep 28 '16 21:09 mauricebaruk

Looks like you're close to solving this! The Exiftool output shows 0x0384 "Date/Time Original", along with some other values from that range.

It'd be useful to see where in the structure Exiftool reads this value. I suspect that there's a tag in the data that links to another IFD that isn't processed.

FILE: FLIR Vue Pro 640.jpg

[JPEG - 0xfffffffd] Compression Type = Baseline
[JPEG - 0x0000] Data Precision = 8 bits
[JPEG - 0x0001] Image Height = 512 pixels
[JPEG - 0x0003] Image Width = 640 pixels
[JPEG - 0x0005] Number of Components = 3
[JPEG - 0x0006] Component 1 = Y component: Quantization table 0, Sampling factors 2 horiz/2 vert
[JPEG - 0x0007] Component 2 = Cb component: Quantization table 1, Sampling factors 1 horiz/1 vert
[JPEG - 0x0008] Component 3 = Cr component: Quantization table 1, Sampling factors 1 horiz/1 vert

[JFIF - 0x0005] Version = 1.2
[JFIF - 0x0007] Resolution Units = inch
[JFIF - 0x0008] X Resolution = 96 dots
[JFIF - 0x000a] Y Resolution = 96 dots
[JFIF - 0x000c] Thumbnail Width Pixels = 0
[JFIF - 0x000d] Thumbnail Height Pixels = 0

[Exif IFD0 - 0x010f] Make = FLIR
[Exif IFD0 - 0x011a] X Resolution = 72 dots per inch
[Exif IFD0 - 0x011b] Y Resolution = 72 dots per inch
[Exif IFD0 - 0x0128] Resolution Unit = Inch
[Exif IFD0 - 0x0213] YCbCr Positioning = Center of pixel array

[Exif SubIFD - 0x9000] Exif Version = 2.10
[Exif SubIFD - 0x9101] Components Configuration = YCbCr
[Exif SubIFD - 0x9214] Subject Location = 320 256 640 512
[Exif SubIFD - 0xa000] FlashPix Version = 1.00
[Exif SubIFD - 0xa001] Color Space = sRGB
[Exif SubIFD - 0xa002] Exif Image Width = 640 pixels
[Exif SubIFD - 0xa003] Exif Image Height = 512 pixels

[GPS - 0x0000] GPS Version ID = 2.200
[GPS - 0x0001] GPS Latitude Ref = S
[GPS - 0x0002] GPS Latitude = 0° 0' 0"
[GPS - 0x0003] GPS Longitude Ref = W
[GPS - 0x0004] GPS Longitude = 0° 0' 0"
[GPS - 0x0005] GPS Altitude Ref = Sea level
[GPS - 0x0006] GPS Altitude = 0 metres
[GPS - 0x0007] GPS Time-Stamp = 15:29:41.000 UTC
[GPS - 0x000c] GPS Speed Ref = kph
[GPS - 0x000d] GPS Speed = 0
[GPS - 0x000e] GPS Track Ref = True direction
[GPS - 0x000f] GPS Track = 0 degrees

- JPEG
- JFIF
- Exif IFD0
    - Exif SubIFD
    - GPS

An alternative explanation is that there's some data in a different APP segment of the JPEG.

How did you come up with offset 0xA0E70?

drewnoakes avatar Sep 28 '16 22:09 drewnoakes

I found really usefull to run exiftool with extra verbose option: exiftool -H -v3 FLIR.jpg what give a really detailed output: https://gist.github.com/novolog/03071937613b9ec0c6424c426f15a53f

As we can see line 740 to 742, there are all the date/time original related stuff. So I used an hexadecimal editor, opened the photo and looked for the actual value a6 7e 71 57 29 00 00 00 f0 00. And found the address. The offset of 0xA0E70 is not the one that fit the photo I pulled, the proper one is 0xA0816.

Here is FLIR.pm from exiftool: https://gist.github.com/novolog/644a4bcd4bec2788d1e911243000aa5f I think this relate of a FFF Header (not sure what it is about), where we can find 0x384 as relative offset in that segment ? I think this chunk of code may be really usefull

I just started today to work with TIFF, EXIF and all that stuff :) I may be wrong in naming stuff.

mauricebaruk avatar Sep 28 '16 23:09 mauricebaruk

That set of Exiftool options produces great output. Very nice, thanks.

So yes it seems that this FLIR image contains a series of APP1 segments (twelve of them, totalling ~643kB), much of which looks like a RAM dump. However the Exiftool code suggests there is a new structure here to interpret. It should be relatively straightforward to work it out and add support and I encourage you to look into extending the library and making a PR. You seem to have a good grasp on the fundamentals.

drewnoakes avatar Sep 29 '16 00:09 drewnoakes

I would be happy to contribute :) For now I feel a bit lost (I'm new to file/image formats and new to java too, but no worry I'm strong in OOP). Do you have any advice to help me to step up faster ? Perhaps do you have some guidelines you could share with me for this implementation ?

mauricebaruk avatar Sep 29 '16 11:09 mauricebaruk

I would be happy to help you. I'll write some guidelines up for the project wiki. I'm a bit short on time today but will try and get something to you soon.

You'll notice there's a pattern of paired *Directory and *Descriptor classes. The directory class lists known tags, keyed by integers. Often these integers are available in the file itself, but other times you have to make them up yourself -- just so long as they're unique within that directory. The descriptor class provides human-friendly string versions of tag values.

Then you have reader classes. These take a stream of data and add zero or more directories of tag values into the Metadata object.

It looks like the data for this FLIR file is located in APP1 segments. Most manufacturer-specific data is stored within the Exif data as 'makernotes', so this FLIR format is a little unique. But that's fine.

Currently the library is ignoring these APP1 segments in FLIR files as it doesn't consider them as containing metadata.

I believe you'll end up with a class such as com.drew.metadata.flir.FlirReader which implements JpegSegmentMetadataReader and returns JpegSegmentType.APP1 from getSegmentTypes(). You would then register it in JpegMetadataReader.ALL_READERS. After that, calling ImageMetadataReader will result in your reader being called for any JPEG files containing APP1 segments.

Your reader will be called via readJpegSegments, to which all APP1 segments in the file are passed in one go. For some formats, this is important. There's a 64kB limit on segment sizes in JPEG (length is only two bytes). Some formats use multiple segments of the same type, and readers are expected to concatenate them together. I'm not sure that's happens with FLIR, but it's not uncommon.

At that point you're really just working with regular Java code and the raw data from the FLIR segments. You can refer to other sources (there's a nice list at the top of the gist you posted above) for information on the format itself. You'll have Iterable<byte[]> to work with, and you might like to use a com.drew.lang.*Reader class to read out multi-byte values conveniently.

Hopefully that should get you a fair way along.

drewnoakes avatar Sep 29 '16 14:09 drewnoakes

I will look at that in the next days. :) Thank you for theses guidelines. For sure if you publish more in the wiki, I will read it.

mauricebaruk avatar Sep 29 '16 18:09 mauricebaruk

Drew -- did this ever go anywhere? I was unable to find your notes on the implementation of any code for a FlirReader. I have started mocking this out... should this be at the makernote level or the Exif level?

hijackak avatar Dec 12 '16 22:12 hijackak

did this ever go anywhere?

@novolog, did you make any progress with this?

should this be at the makernote level or the Exif level?

I'm not sure -- you'll have to do some research. See my comments about APP1 segments above, as there seemed to be a bunch of non-TIFF data there. The PDF file linked in the original issue suggests that DateTime Original should be present in the Exif data though. It may be that they use non-standard tags to link to other IFDs containing the desired data.

If you could provide more samples images from FLIR camera(s), that would be useful too.

drewnoakes avatar Dec 13 '16 11:12 drewnoakes

We have a reference implementation on the .NET side if anyone wants to tackle porting this to Java:

https://github.com/drewnoakes/metadata-extractor-dotnet/pull/295

drewnoakes avatar Jun 07 '21 03:06 drewnoakes