Skip to content

Repository files navigation

Fluke Thermal Reader

A Python library for reading and analyzing Fluke thermal imaging files: full support for .is2 (still images), partial support for .is3 (video — container and metadata; the thermal video codec itself is proprietary and undocumented, see below).

Package: fluke-thermal-reader · Import: import fluke_thermal_reader or from fluke_thermal_reader import read_is2


Features

  • .is2 reading: Full parsing of Fluke thermal imaging files (.is2)
  • .is3 reading (partial): extracts the visible-light H.264 video track and camera/calibration metadata from Fluke thermal video files; see read_is3 below
  • Temperature conversion: Raw counts to temperature (°C) with emissivity and reflected background correction (radiative formula)
  • Metadata: Camera model, dimensions, emissivity, transmission, background temperature, min/max/avg from file and from JSON when present
  • Command-line interface: fluke_thermal_reader <file> [--info|--stats|--export-csv] after installing
  • Minimal dependencies: Only numpy
  • Tested and working: Fluke Ti480P, Ti300, TiS75+; PTi120 parses but is unverified (see below)

Installation

From PyPI

pip install fluke-thermal-reader

Quick start

from fluke_thermal_reader import read_is2

# Load a .is2 file
data = read_is2("thermal_image.is2")

# Thermal matrix (2D, °C)
thermal_data = data["data"]
print(f"Temperature range: {thermal_data.min():.1f}°C - {thermal_data.max():.1f}°C")

# Metadata
print(f"Camera: {data['CameraModel']}")
print(f"Size: {data['size']}")  # [width, height]
print(f"Emissivity: {data['Emissivity']}")
print(f"Background temperature: {data['BackgroundTemp']}°C")

Plot with matplotlib

import matplotlib.pyplot as plt
from fluke_thermal_reader import read_is2

data = read_is2("thermal_image.is2")
plt.imshow(data["data"], cmap="coolwarm", aspect="equal")
plt.colorbar(label="Temperature (°C)")
plt.title(f"Thermal image — {data['CameraModel']}")
plt.show()

Full example script (basic_usage_example.py)

A more complete, ready-to-run example is provided in basic_usage_example.py at the repository root.
It will:

  • Ask you to select a .is2 file via a file dialog
  • Print basic metadata and temperature statistics
  • Show the thermal image with a blue→red colormap and markers for the coldest (MIN) and hottest (MAX) pixels
python basic_usage_example.py

Returned data structure (read_is2)

Key Type Description
data 2D ndarray Temperature in °C per pixel
FileName str File name
CameraModel str Thermal camera model
CameraSerial str Serial number
size [w, h] Image dimensions
MinTemp, MaxTemp, AvgTemp float From file/JSON when present
Emissivity float Emissivity
Transmission float Transmission
BackgroundTemp float Background temperature
thumbnail_path str / None Thumbnail path (if present)
photo_path str / None Visible photo path (if present)

Requirements

  • Python 3.8+
  • numpy >= 1.20.0

For visualization: matplotlib (optional).


Tested camera models

Tested and working with:

  • Fluke Ti480P
  • Fluke Ti300
  • Fluke TiS75+ — verified against 4 reference exports (mean error 0.4-4.4°C depending on file). Its file format has no real embedded calibration curve or usable temperature range, so readings use a fixed slope (stable across all 4 references) plus the file's own background-temperature field as the best available per-file offset — not a perfect per-file calibration, but close.
  • Fluke PTi120 — verified against a reference export (mean error ~0.07°C). Its calibration data has no real embedded curve, so temperature comes from a fixed linear scale fit against that one reference rather than from data in the file itself — accuracy on other units/scenes isn't guaranteed; feedback (and more reference exports) welcome if you hit an inaccurate reading.

Other Fluke .is2 files may work; feedback and sample files for additional models are welcome.


Project structure

Fluke_Python/
├── fluke_thermal_reader/    # Main package
│   ├── __init__.py
│   ├── reader.py            # read_is2, read_is3, FlukeReader
│   ├── parsers.py           # IS2 parser
│   ├── is3_parser.py        # IS3 (Matroska video) parser — visible track + metadata only
│   ├── camera_profiles.py   # per-model profile registry
│   ├── utilities.py         # UnitConversion, calc_equation
│   └── cli.py
├── docs/
│   └── is3_video_codec_notes.md  # V_FLUKE/HUFF reverse-engineering notes
├── basic_usage_example.py   # Full example script (CLI + plot)
├── requirements.txt
└── README.md

Command-line interface

Installing the package (pip install fluke-thermal-reader or an editable install, see below) provides a fluke_thermal_reader command:

fluke_thermal_reader thermal_image.is2 --info --stats
fluke_thermal_reader thermal_image.is2 --export-csv output.csv
fluke_thermal_reader thermal_video.is3 --info

Without any flags it prints a short summary (image size and average temperature, or video duration/frame count for .is3).


Development and testing

# Editable install with dev dependencies (pytest, black, flake8, mypy)
pip install -e ".[dev]"

# Run the test suite
pytest -v

# Run a single test
pytest tests/test_reader.py::test_read_is2_file_not_found

# Format / lint / type-check
black .
flake8
mypy fluke_thermal_reader

See Publish_to_PiPy.md for the release process.


read_is3 (partial support)

.is3 files are Matroska (MKV) video containers with two tracks: a standard H.264 visible-light video, and a thermal track using a proprietary, undocumented codec (V_FLUKE/HUFF).

from fluke_thermal_reader import read_is3

video = read_is3("thermal_video.is3")
print(video["CameraModel"], video["FrameCount"], video["Duration"])
print(video["visible_video_path"])  # extracted H.264 elementary stream (mux with ffmpeg if needed)
  • Visible-light video: extracted and written out as a raw H.264 (.h264) file next to the source file (or to output_dir if passed to read_is3). Mux to .mp4 with e.g. ffmpeg -i video_visible.h264 -c copy video.mp4.
  • Camera model / calibration metadata: extracted from the container's attachment.
  • Thermal data: not implemented. video["thermal_data"] is always None; video["thermal_status"] explains why (V_FLUKE/HUFF has no known public specification). See docs/is3_video_codec_notes.md for a detailed writeup of what's been reverse-engineered so far — the container and per-frame table format are understood, but not the codec itself.

If you can help identify the V_FLUKE/HUFF format (an SDK, a spec, a reference decoder), please open an issue.


License

See the LICENSE file in the repository.


Changelog

0.3.0 (2026-07-29)

  • Fix (correctness, TiS75+): TiS75+ thermal frames were being extracted with the wrong strategy (a raw-uint16-blob fallback instead of the varint-protobuf format it actually uses, the same format as PTi120) and, separately, any negative decoded value was treated as an invalid pixel instead of only the real -1 sentinel — together these produced 60-77°C mean errors against reference exports while still "looking" plausible (a smooth 22-95°C-ish image). Both are fixed; remaining error after the fix is 0.4-4.4°C, limited by the lack of a real per-file calibration in the format itself (see CLAUDE.md). This is why "looks plausible" was never sufficient validation for this file format, and TiS75+ regression tests with real reference exports are now part of the test suite.
  • Fix (correctness): CalibrationData.gpbenc can contain multiple calibration tables (e.g. standard vs. extended high-temperature range); the parser now picks the correct one instead of silently merging them, which previously produced errors of 300°C+ on Ti300 files with a near-ambient scene. A similar mismatch is still unresolved on at least one Ti480P case — see CLAUDE.md for details if you hit this.
  • Fix: image dimensions could be inferred from the wrong bundled JPEG (a full-resolution visible-light photo instead of the IR-registered image) when no metadata was present, corrupting the parsed size.
  • Fix: the fluke_thermal_reader command-line interface was broken (written against an unused dataclass API); it now works against the same dict read_is2()/read_is3() return, and is installed as a real console-script entry point (pip install gives you the fluke_thermal_reader command, not just python -m fluke_thermal_reader.cli).
  • Add: partial .is3 (video) support — read_is3() extracts the visible-light H.264 track and camera/calibration metadata. Thermal video decoding is not implemented: the codec (V_FLUKE/HUFF) is proprietary and undocumented; see docs/is3_video_codec_notes.md for what's been reverse-engineered so far.
  • Add: initial support for Fluke PTi120 (CalTempDataRex.gpbenc stored as a real protobuf message with an unpacked repeated-varint field, rather than a raw uint16 blob). Temperature accuracy verified against a reference export (mean error ~0.07°C) using a fixed linear scale fit to that reference, since the file itself has no real embedded calibration curve — see the Tested camera models section above.
  • Fix: a Ti300 sample with a small overexposed highlight had a ~1.6°C mean / ~23°C peak error from an unresolved calibration-curve edge case near the top of its range; not root-caused, but the regression test now tracks it explicitly instead of silently allowing it to grow.
  • Perf: vectorized the calibration lookup-table construction with numpy.
  • Packaging: fixed the repository so pyproject.toml/MANIFEST.in/release scripts are actually tracked and installable from a clean checkout (previously excluded by a .gitignore mistake).

0.2.0

  • Stable .is2 parser with temperature conversion (emissivity + background temperature)

0.1.x

  • Initial release, basic .is2 support

About

A Python library for reading and analyzing Fluke thermal files (.is2 and .is3 formats).

Topics

Resources

Stars

4 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages