Convert DJI thermal R-JPEGs into FLIR-format radiometric JPEGs that open directly in FLIR Tools, FLIR Thermal Studio, and ResearchIR with full per-pixel temperature data intact.
Ships as a single double-clickable Windows .exe. No Python install,
no DJI SDK download, no command line. Pick a folder, get a folder.
Download DJI-to-FLIR.exe (latest release) · All releases
Windows 10 / 11, 64-bit. Self-contained — nothing else to install.
| Before (DJI R-JPEG) | After (FLIR-format radiometric) |
|---|---|
![]() |
![]() |
The "after" file is a real FLIR-format R-JPEG that opens in FLIR Tools / Thermal Studio / ResearchIR with the full radiometric stream intact. Round-trip temperature error versus the DJI SDK's own decoded values is < 0.01 °C on test M4T captures.
- Click Convert a folder…
- Pick a folder of DJI photos (mixed visible + thermal + videos is fine).
- Wait for the progress bar.
- Output appears next to the source folder as
<folder name>_FLIR/.
That's it. No checkboxes, no path-typing, no format pickers. Native Windows tkinter look-and-feel, runs entirely offline.
Supported cameras: H30T, H20T, M30T, M3T, M4T, M4TD.
DJI and FLIR both store "radiometric" thermal JPEGs — every pixel encodes temperature, not just a color — but they use completely different on-disk formats with no published mapping between their two calibration systems:
- DJI packs raw thermal data into a proprietary APP3 segment that
only DJI's own SDK (
libdirp.dll) can decode. The decoder applies the camera's per-unit IIRP calibration internally. - FLIR packs raw thermal data into an "FFF" container split across
one or more APP1 segments, alongside the Planck constants
(
R1,B,F,O,R2), atmospheric coefficients (ATA1/2,ATB1/2,ATX), and scene parameters (emissivity, distance, reflected/atmospheric temp, humidity).
DJI's tools won't open FLIR files; FLIR's tools won't open DJI files.
The catch that breaks the obvious approaches: ExifTool marks every
FLIR radiometric tag as writable = false in FLIR.pm. You can read
the tags out of an existing FLIR file but you cannot write them into
one. A naïve "extract DJI temperatures, write FLIR tags via ExifTool"
pipeline is impossible.
The converter uses a real FLIR R-JPEG (Thermimage's IR_2412.jpg, MIT
license) as a structural template. For each DJI input:
- Extract factory-grade temperatures via the DJI Thermal SDK v1.8
over a
ctypeswrapper — applies the camera's IIRP calibration. - Re-encode to FLIR's raw uint16 by inverting Thermimage's published Planck equation against the template's calibration.
- Walk the JPEG segment chain to extract the template's FLIR APP1 segments and reassemble them into a single FFF blob.
- Parse the FFF directory (mixed-endian: BE header + index, LE
record bodies) to locate the
RawDatarecord (0x0001) andCameraInforecord (0x0020). - Patch RawData in place with the new uint16 stream, re-split the FFF back into APP1-sized chunks (≤ 65 520 bytes per the JPEG segment-length cap), re-inject into the JPEG bytestream.
- Patch CameraInfo's sensor dimensions so FLIR Thermal Studio interprets the raw stream correctly when the DJI sensor size differs from the template's source camera.
- Pass through EXIF/GPS via a bundled ExifTool — only tags ExifTool can write (DateTimeOriginal, GPS, Model).
Planck calibration, atmospherics, emissivity, distance, reflected temperature, and humidity are left at the template's values — deliberately, because those are exactly what FLIR Tools / Thermal Studio expects, and the user adjusts them post-hoc in FLIR's software just like with a native FLIR capture.
If the SDK can't decode a file (corrupt input, or a camera not in the v1.8 support list) it's flagged as an error and skipped rather than silently degraded — accuracy is the whole point.
main.py # tkinter UI (single window, three states)
build.spec # PyInstaller --onefile spec
requirements.txt
converter/
pipeline.py # folder walk + per-file orchestration
tsdk.py # libdirp.dll ctypes wrapper (SDK v1.8)
tier2_flir_jpeg.py # FLIR R-JPEG binary builder (~925 lines)
camera_profiles.py # per-camera sensor metadata
dji_app3.py # APP3 raw-thermal segment reader (diagnostic)
dji_iirp.py # IIRP per-camera calibration reader
flir_format/
planck.py # temp ↔ raw uint16 (Thermimage formulas)
tags.py # FLIR EXIF/XMP tag names
template.jpg # canonical FLIR R-JPEG template
examples/ # before/after sample images
docs/BUILD.md # how to build the .exe from source
Bundled at build time (license / size reasons, not in this repo):
tsdk_dlls/— DJI Thermal SDK v1.8 binaries (requires DJI dev account)exiftool.exe+exiftool_files/— ExifTool's Windows distribution
See docs/BUILD.md.
Python 3.11+ · NumPy · Pillow · piexif · ctypes for the DJI Thermal
SDK · tkinter for the native UI · PyInstaller --onefile for the
self-contained Windows distribution.
- Thermimage (R, G. Tattersall, GPL-3) — Planck equation reference + canonical
IR_2412.jpgtemplate — https://github.com/gtatters/Thermimage - flirpy (LJMU Astroecology) — working FLIR R-JPEG reader — https://github.com/LJMUAstroecology/flirpy
- ExifTool FLIR.pm — canonical FLIR tag-name reference — https://exiftool.org/TagNames/FLIR.html
- kentavv/flir-batch-process — alternate FLIR R-JPEG processor — https://github.com/kentavv/flir-batch-process
MIT — see LICENSE.
The DJI Thermal SDK and ExifTool are not redistributed in this repo;
they're pulled in at build time on the developer's machine. The
end-user .exe bundles them under their respective licenses.

