LJMUAstroecology / flirpy

Python library to interact with FLIR camera cores
Other
191 stars 54 forks source link

error converting A655SC seq file to readable images #43

Closed autimator closed 10 months ago

autimator commented 3 years ago

Hello,

Using Python 3.8.7 on a windows 10 laptop With this command: python split_seqs.py --no_sync_rgb --no_export_preview --no_export_tiff I can get the raw data: '.ttt' files and the metadata .txt files out of the seq file.

When I try to get the thermal images, running like this: python split_seqs.py --no_sync_rgb --no_export_preview I get the following error:

Traceback (most recent call last): File "split_seqs.py", line 110, in <module> folders = splitter.process(files) File "C:\Users\daale010\AppData\Local\Programs\Python\Python38\lib\site-packages\flirpy\io\seq.py", line 79, in process self._process_seq(seq, folder) File "C:\Users\daale010\AppData\Local\Programs\Python\Python38\lib\site-packages\flirpy\io\seq.py", line 200, in _process_seq image = frame.get_radiometric_image(meta) File "C:\Users\daale010\AppData\Local\Programs\Python\Python38\lib\site-packages\flirpy\io\fff.py", line 49, in get_radiometric_image image = raw2temp(self.get_image(), meta) File "C:\Users\daale010\AppData\Local\Programs\Python\Python38\lib\site-packages\flirpy\io\fff.py", line 56, in get_image offset = self._find_data_offset(self.data) File "C:\Users\daale010\AppData\Local\Programs\Python\Python38\lib\site-packages\flirpy\io\fff.py", line 46, in _find_data_offset return res.end()+14 AttributeError: 'NoneType' object has no attribute 'end'

I uploaded the file to wetransfer, https://we.tl/t-2WoLGFZzAD

I hope its something small and I can use your library to process images from this camera.

jveitchmichaelis commented 3 years ago

This looks like an endianness problem. What happens if you split the FFF files and then run them through Exiftool?

Also are you using flirpy from github, or pip?

Thanks for the file, I'll see if I can get it working, but I can't promise when I'll have time.

autimator commented 3 years ago

Thank you for the quick reply!

I pip installed flirpy and manually downloaded split_seqs.py, to process the files. Any help would be appreciated, everything is better than processing the files by hand.

When I have the FFF file, I can convert it to a tif with exiftool: exiftool -b -RawThermalImage 1.fff > 1.tif

and then imagemagick to make the tif readable : Magick.exe -define tiff:swap-bytes=on 1.tif 2.tif Magick.exe 2.tif -auto-level 3.tif

and then I could add the thermal matrix extraction code from this library, which I use for my FLIR AX8: https://github.com/nationaldronesau/FlirImageExtractor/blob/master/flir_image_extractor_cli/flir_image_extractor.py

If I combine all these steps in one script I am basically building my own image processing library. While I suspect your library should work with a few small changes. So if you have any tips on how to modify your code for the A655SC (which files to check/modify) or if you could give it a try that would save me and many others days of work.

autimator commented 3 years ago

It took more time than I hoped but I managed to transform the flirpy code into a program which works for the flir SC655. I had to change quite a lot so it's not something I can easily add in a pull request, but I will explain my changes below for others who might run into the same issue.

Change one is the resolution: python split_seqs.py --width 640 --height 480 (Ideally you would get the resolution from the seq file directly, before splitting it.)

Change two is that I save the frame to tiff files in seq.py after: frame = FFF(chunk, height=self.height, width=self.width) And extract the raw data from it using:

def extract_bytes(self, FFFFile, datafile):
        #cwd = os.path.dirname(FFFFile)
        cmd = [('"' + self.path + '"')]
        cmd.append("-b")
        cmd.append("-RawThermalImage")
        cmd.append('"' + FFFFile + '"')
        cmd.append(">")
        cmd.append('"' + datafile + '"')

        command = " ".join(cmd)
        logger.debug(" ".join(cmd))
        res = subprocess.run(command, capture_output=True, shell=True)#has to run in shell
        print('Return code:', res.returncode)
        # use decode function to convert to string
        return res.returncode

this is mentioned in the meta file: 'Raw Thermal Image : (Binary data 614604 bytes, use -b option to extract)'

Change three the data offset is different for the flir SC655: in find_data_offset in fff.py:

def _find_data_offset(self, data):  
        search = b'\x00H\x00\x00\x00\x01\x00\x00\x00H\x00\x00\x00\x01\x00\x00\x00'
        valid = re.compile(search)
        res = valid.search(data)

        return res.end()#+14

Change four sometimes there are units in the meta file of the SC655 In raw2temp in had to change these lines:

    IRWTemp = float(meta["IR Window Temperature"].split()[0])#drop the unit
    OD = float(meta["Object Distance"].split()[0])
    ATemp = float(meta["Atmospheric Temperature"].split()[0])
    RTemp = float(meta["Reflected Apparent Temperature"].split()[0])
    humidity = float(meta["Relative Humidity"].split()[0])

And I removed the following lines as I want the temperature in degrees (don't understand why you divide by 0.04 but the temperatures look good)

image += 273.15 # Convert to Kelvin
image /= 0.04

I hope these notes can help others get it working as well

jveitchmichaelis commented 3 years ago

Thanks for putting the effort in here, I can probably merge some of these changes. Some of this is already implemented, for example getting the image size from the header. The main issue is I'm trying to remove the dependency on Exiftool because it's a bit faster to do everything in Python without having to call external software (and it allows you to do stuff like index into SEQ files directly).

This also means that some things like the data offset should be determined from the SEQ header, rather than trying to grep for a magic byte sequence. I've had a couple of requests from other cameras and the main issue seems to be that the endianness changes with some cameras, so the data offsets are totally wrong (but this should be detectable).

And I removed the following lines as I want the temperature in degrees (don't understand why you divide by 0.04 but the temperatures look good)

My guess is that working in Kelvin means that all your numbers are positive. Otherwise every frame would have to have some offset. 0.04 is FLIR's choice of scaling value to convert 16-bit unsigned into a float range; if you run your camera in high/low gain modes this also changes.

autimator commented 3 years ago

That sounds really good, my current code needed 20 minutes on a fast PC to process the SEQ file I uploaded (only 400 images), now is this all running in one thread. You could brute force it by running 8 or 16 at the same time, than it takes less than 2 minutes to process the file. Having said that, reading parts of SEQ files and doing all the processing in python is definitely the better solution!

With this SEQ file I had no endianness problems in the end, Exiftool probably solved this in the extract_bytes() call. I also did not need the division by 0.04, the temperature values already looked good after the raw2temp function, maybe Exiftool also worked some magic here? or that’s another difference between the camera’s.

And I did learn that saving the temperature values off each frame in a separate CSV file is a bad idea. Later it takes ages to load all those CSV files, as solution I am thinking of storing them as rows in a SQL lite table. That will also make it easier to pull out averages and do other analysis on all frames (and provides the option to add a few columns with meta data, like the time the image was taken.)

jveitchmichaelis commented 1 year ago

Hi @autimator this is pretty late, but this should now be fixed. If you still have some data, it would be really useful to check that you can load it properly with the new Fff reader. The code now (hopefully) correctly identifies FFF records, handles the big/littleendian weirdness, and should load the image + camera info appropriately.

autimator commented 10 months ago

Heey @jveitchmichaelis, thank you for all your hard work. There is no official new release which made your latest version a bit tricky to install but I managed. To help others get started with commit bfa3da7 on Jul 4

I downloaded the project and moved 'scr/flirpy' to 'flirpy'. After this I installed the requirements.txt and added the code below in a main.py. Flirpy extracts the individual FFF frames from the seq file and converts them to numpy arrays with the temperature values. A huge time saver.

from pathlib import Path
from flirpy.io.seq import Splitter
from flirpy.io.fff import Fff as FFF
import matplotlib.pyplot as plt

if __name__ == "__main__":
    input_folder = Path.cwd()
    input_file = Path.joinpath(input_folder, 'Rec-000012.seq')
    output_folder = Path('output')
    # Part one: extract frames from seq file
    sp = Splitter(str(output_folder))
    sp.split_folders = True
    sp.process(str(input_file))

    # Part two: frame to temperature values
    frames = output_folder.joinpath('Rec-000012','raw')

    for frame in frames.glob('*.fff'):
        data = FFF(str(frame))
        radiometric = data.get_radiometric_image()

        plt.imshow(radiometric, cmap='magma')
        plt.colorbar()
        plt.show()