fluke-thermal-reader 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- fluke_thermal_reader-0.1.0/MANIFEST.in +5 -0
- fluke_thermal_reader-0.1.0/PKG-INFO +318 -0
- fluke_thermal_reader-0.1.0/README.md +285 -0
- fluke_thermal_reader-0.1.0/examples/basic_usage.py +96 -0
- fluke_thermal_reader-0.1.0/examples/batch_processing.py +188 -0
- fluke_thermal_reader-0.1.0/examples/readis2.py +36 -0
- fluke_thermal_reader-0.1.0/examples/simple_usage.py +82 -0
- fluke_thermal_reader-0.1.0/fluke_thermal_reader/__init__.py +35 -0
- fluke_thermal_reader-0.1.0/fluke_thermal_reader/cli.py +114 -0
- fluke_thermal_reader-0.1.0/fluke_thermal_reader/models.py +57 -0
- fluke_thermal_reader-0.1.0/fluke_thermal_reader/parsers.py +374 -0
- fluke_thermal_reader-0.1.0/fluke_thermal_reader/reader.py +177 -0
- fluke_thermal_reader-0.1.0/fluke_thermal_reader.egg-info/PKG-INFO +318 -0
- fluke_thermal_reader-0.1.0/fluke_thermal_reader.egg-info/SOURCES.txt +25 -0
- fluke_thermal_reader-0.1.0/fluke_thermal_reader.egg-info/dependency_links.txt +1 -0
- fluke_thermal_reader-0.1.0/fluke_thermal_reader.egg-info/requires.txt +12 -0
- fluke_thermal_reader-0.1.0/fluke_thermal_reader.egg-info/top_level.txt +1 -0
- fluke_thermal_reader-0.1.0/pyproject.toml +62 -0
- fluke_thermal_reader-0.1.0/requirements.txt +1 -0
- fluke_thermal_reader-0.1.0/setup.cfg +4 -0
- fluke_thermal_reader-0.1.0/test/test_all_parameters.py +289 -0
- fluke_thermal_reader-0.1.0/test/test_emissivity_differences.py +240 -0
- fluke_thermal_reader-0.1.0/test/test_fluke_reader.py +101 -0
- fluke_thermal_reader-0.1.0/test/test_gh_file.py +64 -0
- fluke_thermal_reader-0.1.0/test/test_transmission.py +305 -0
- fluke_thermal_reader-0.1.0/tests/__init__.py +2 -0
- fluke_thermal_reader-0.1.0/tests/test_reader.py +127 -0
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: fluke_thermal_reader
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Libreria Python per leggere file termografici Fluke (.is2 e .is3)
|
|
5
|
+
Author-email: Lorenzo Ghidini <lorigh46@gmail.com>
|
|
6
|
+
Project-URL: Homepage, https://github.com/LoriGH25/FlukeReader_Python
|
|
7
|
+
Project-URL: Repository, https://github.com/LoriGH25/FlukeReader_Python
|
|
8
|
+
Project-URL: Issues, https://github.com/LoriGH25/FlukeReader_Python/issues
|
|
9
|
+
Keywords: fluke,thermal,thermography,is2,is3,temperature
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Intended Audience :: Science/Research
|
|
12
|
+
Classifier: Operating System :: OS Independent
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Topic :: Scientific/Engineering :: Physics
|
|
20
|
+
Classifier: Topic :: Scientific/Engineering :: Image Processing
|
|
21
|
+
Requires-Python: >=3.8
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
Requires-Dist: numpy>=1.20.0
|
|
24
|
+
Provides-Extra: dev
|
|
25
|
+
Requires-Dist: pytest>=6.0; extra == "dev"
|
|
26
|
+
Requires-Dist: pytest-cov; extra == "dev"
|
|
27
|
+
Requires-Dist: black; extra == "dev"
|
|
28
|
+
Requires-Dist: flake8; extra == "dev"
|
|
29
|
+
Requires-Dist: mypy; extra == "dev"
|
|
30
|
+
Provides-Extra: docs
|
|
31
|
+
Requires-Dist: sphinx; extra == "docs"
|
|
32
|
+
Requires-Dist: sphinx-rtd-theme; extra == "docs"
|
|
33
|
+
|
|
34
|
+
# Fluke Thermal Reader
|
|
35
|
+
|
|
36
|
+
A Python library for reading and analyzing Fluke thermal files (.is2 and .is3 formats).
|
|
37
|
+
|
|
38
|
+
**Package**: `fluke_thermal_reader` | **Import**: `import fluke_thermal_reader`
|
|
39
|
+
|
|
40
|
+
## Features
|
|
41
|
+
|
|
42
|
+
- **Complete .is2 support**: Parse Fluke .is2 thermal imaging files
|
|
43
|
+
- **Accurate temperature conversion**: Convert raw thermal data to temperature values with high precision
|
|
44
|
+
- **Metadata extraction**: Extract camera information, calibration data, and image properties
|
|
45
|
+
- **Fusion offset correction**: Automatic correction for horizontal shift in thermal images
|
|
46
|
+
- **Lightweight design**: Minimal dependencies (only numpy required)
|
|
47
|
+
- **Optional image loading**: Images are returned as paths, not loaded automatically
|
|
48
|
+
- **Tested camera models**: Ti300 and Ti480P (other models supported with user feedback)
|
|
49
|
+
|
|
50
|
+
## Installation
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
pip install fluke_thermal_reader
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
**Important**: package name and import are identical: `fluke_thermal_reader`.
|
|
57
|
+
|
|
58
|
+
## Quick Start
|
|
59
|
+
|
|
60
|
+
```python
|
|
61
|
+
from fluke_thermal_reader import read_is2
|
|
62
|
+
|
|
63
|
+
# Load a Fluke .is2 file
|
|
64
|
+
data = read_is2("thermal_image.is2")
|
|
65
|
+
|
|
66
|
+
# Access thermal data
|
|
67
|
+
thermal_data = data['data'] # 2D numpy array of temperatures
|
|
68
|
+
print(f"Temperature range: {thermal_data.min():.1f}°C - {thermal_data.max():.1f}°C")
|
|
69
|
+
|
|
70
|
+
# Access metadata
|
|
71
|
+
print(f"Camera: {data['CameraModel']}")
|
|
72
|
+
print(f"Image size: {data['size']}")
|
|
73
|
+
print(f"Emissivity: {data['Emissivity']}")
|
|
74
|
+
print(f"Background temperature: {data['BackgroundTemp']}°C")
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Supported File Formats
|
|
78
|
+
|
|
79
|
+
- **IS2 (images)**: Fluke thermal image format — FULLY SUPPORTED
|
|
80
|
+
- **IS3 (video)**: Fluke thermal video format — FUTURE WORK (not implemented yet)
|
|
81
|
+
|
|
82
|
+
All the examples and usage below refer to IS2 image files only.
|
|
83
|
+
|
|
84
|
+
## Image Handling
|
|
85
|
+
|
|
86
|
+
The library is designed to be lightweight and efficient. Images (thumbnails and visible photos) are handled as follows:
|
|
87
|
+
|
|
88
|
+
- **Paths only**: Images are returned as file paths, not loaded automatically
|
|
89
|
+
- **Optional loading**: You choose when and how to load images
|
|
90
|
+
- **Memory efficient**: No automatic image loading saves memory
|
|
91
|
+
- **Flexible**: Use any image library you prefer (matplotlib, PIL, opencv, etc.)
|
|
92
|
+
|
|
93
|
+
```python
|
|
94
|
+
# Images are returned as paths
|
|
95
|
+
data = read_is2("thermal_image.is2")
|
|
96
|
+
print(f"Thumbnail path: {data['thumbnail_path']}")
|
|
97
|
+
print(f"Photo path: {data['photo_path']}")
|
|
98
|
+
|
|
99
|
+
# Load images only when needed
|
|
100
|
+
if data['thumbnail_path']:
|
|
101
|
+
# Option 1: Using matplotlib
|
|
102
|
+
import matplotlib.pyplot as plt
|
|
103
|
+
thumbnail = plt.imread(data['thumbnail_path'])
|
|
104
|
+
|
|
105
|
+
# Option 2: Using PIL (lighter)
|
|
106
|
+
from PIL import Image
|
|
107
|
+
thumbnail = Image.open(data['thumbnail_path'])
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## Tested Camera Models
|
|
111
|
+
|
|
112
|
+
- **Ti300**: Fully tested and supported
|
|
113
|
+
- **Ti480P**: Fully tested and supported
|
|
114
|
+
|
|
115
|
+
**Other Fluke camera models**: If you have files from other Fluke thermal cameras, please share them so we can add support for additional models.
|
|
116
|
+
|
|
117
|
+
## API Reference
|
|
118
|
+
|
|
119
|
+
### `read_is2(file_path)`
|
|
120
|
+
|
|
121
|
+
Load and parse a Fluke thermal .is2 file.
|
|
122
|
+
|
|
123
|
+
**Parameters:**
|
|
124
|
+
- `file_path` (str): Path to the .is2 file
|
|
125
|
+
|
|
126
|
+
**Returns:**
|
|
127
|
+
- `dict`: Dictionary containing thermal data and metadata
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
**Returned Data Structure (read_is2):**
|
|
131
|
+
```python
|
|
132
|
+
{
|
|
133
|
+
'data': numpy.ndarray, # 2D array of temperature values
|
|
134
|
+
'FileName': str, # Original filename
|
|
135
|
+
'CameraModel': str, # Camera model
|
|
136
|
+
'CameraSerial': str, # Camera serial number
|
|
137
|
+
'size': [width, height], # Image dimensions
|
|
138
|
+
'MinTemp': float, # Minimum temperature
|
|
139
|
+
'MaxTemp': float, # Maximum temperature
|
|
140
|
+
'AvgTemp': float, # Average temperature
|
|
141
|
+
'Emissivity': float, # Emissivity setting
|
|
142
|
+
'BackgroundTemp': float, # Background temperature
|
|
143
|
+
'CaptureDateTime': str, # Capture date and time
|
|
144
|
+
'thumbnail_path': str, # Path to thumbnail image (if available)
|
|
145
|
+
'photo_path': str, # Path to visible light image (if available)
|
|
146
|
+
}
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
## Examples
|
|
150
|
+
|
|
151
|
+
### Simple Usage (No External Dependencies)
|
|
152
|
+
|
|
153
|
+
```python
|
|
154
|
+
from fluke_thermal_reader import read_is2
|
|
155
|
+
import numpy as np
|
|
156
|
+
|
|
157
|
+
# Load thermal data
|
|
158
|
+
data = read_is2("thermal_image.is2")
|
|
159
|
+
|
|
160
|
+
# Basic analysis
|
|
161
|
+
temperatures = data['data']
|
|
162
|
+
print(f"Temperature range: {temperatures.min():.1f}°C - {temperatures.max():.1f}°C")
|
|
163
|
+
print(f"Average: {temperatures.mean():.1f}°C")
|
|
164
|
+
|
|
165
|
+
# Hot spot analysis
|
|
166
|
+
hot_spots = temperatures > (temperatures.mean() + 2 * temperatures.std())
|
|
167
|
+
print(f"Hot spots: {np.sum(hot_spots)} pixels")
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### Basic Usage (With Visualization)
|
|
171
|
+
|
|
172
|
+
```python
|
|
173
|
+
from fluke_thermal_reader import read_is2
|
|
174
|
+
import matplotlib.pyplot as plt
|
|
175
|
+
|
|
176
|
+
# Load thermal data
|
|
177
|
+
data = read_is2("thermal_image.is2")
|
|
178
|
+
|
|
179
|
+
# Display thermal image
|
|
180
|
+
plt.imshow(data['data'], cmap='hot')
|
|
181
|
+
plt.colorbar(label='Temperature (°C)')
|
|
182
|
+
plt.title(f'Thermal Image - {data["CameraModel"]}')
|
|
183
|
+
plt.show()
|
|
184
|
+
|
|
185
|
+
# Load images separately if needed (optional)
|
|
186
|
+
if data['thumbnail_path']:
|
|
187
|
+
# Using matplotlib (requires matplotlib)
|
|
188
|
+
thumbnail = plt.imread(data['thumbnail_path'])
|
|
189
|
+
plt.figure()
|
|
190
|
+
plt.imshow(thumbnail)
|
|
191
|
+
plt.title('Thumbnail')
|
|
192
|
+
plt.show()
|
|
193
|
+
|
|
194
|
+
# Or using PIL (lighter alternative)
|
|
195
|
+
# from PIL import Image
|
|
196
|
+
# thumbnail = Image.open(data['thumbnail_path'])
|
|
197
|
+
# thumbnail.show()
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
### Batch Processing
|
|
201
|
+
|
|
202
|
+
```python
|
|
203
|
+
import os
|
|
204
|
+
from fluke_thermal_reader import read_is2
|
|
205
|
+
|
|
206
|
+
# Process multiple files
|
|
207
|
+
for filename in os.listdir("thermal_images/"):
|
|
208
|
+
if filename.endswith(".is2"):
|
|
209
|
+
data = read_is2(f"thermal_images/{filename}")
|
|
210
|
+
print(f"Processed {filename}: {data['MinTemp']:.1f}°C - {data['MaxTemp']:.1f}°C")
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
### Temperature Analysis
|
|
214
|
+
|
|
215
|
+
```python
|
|
216
|
+
import numpy as np
|
|
217
|
+
from fluke_thermal_reader import read_is2
|
|
218
|
+
|
|
219
|
+
# Load data
|
|
220
|
+
data = read_is2("thermal_image.is2")
|
|
221
|
+
temperatures = data['data']
|
|
222
|
+
|
|
223
|
+
# Statistical analysis
|
|
224
|
+
print(f"Temperature statistics:")
|
|
225
|
+
print(f" Mean: {temperatures.mean():.1f}°C")
|
|
226
|
+
print(f" Std: {temperatures.std():.1f}°C")
|
|
227
|
+
print(f" Min: {temperatures.min():.1f}°C")
|
|
228
|
+
print(f" Max: {temperatures.max():.1f}°C")
|
|
229
|
+
|
|
230
|
+
# Find hot spots
|
|
231
|
+
hot_spots = temperatures > (temperatures.mean() + 2 * temperatures.std())
|
|
232
|
+
print(f"Hot spots: {np.sum(hot_spots)} pixels")
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
## Accuracy
|
|
236
|
+
|
|
237
|
+
The library provides highly accurate temperature readings with:
|
|
238
|
+
- **Mean difference**: < 0.5°C compared to Fluke's official software
|
|
239
|
+
- **Correlation**: > 0.999 with reference data
|
|
240
|
+
- **Precision**: 16 decimal places for temperature values
|
|
241
|
+
- **Tested on**: Ti300 and Ti480P cameras with real-world data
|
|
242
|
+
|
|
243
|
+
## Requirements
|
|
244
|
+
|
|
245
|
+
- Python 3.8+
|
|
246
|
+
- numpy>=1.20.0
|
|
247
|
+
|
|
248
|
+
**Optional dependencies for visualization:**
|
|
249
|
+
- matplotlib (for plotting thermal data)
|
|
250
|
+
- PIL/Pillow (for loading images)
|
|
251
|
+
|
|
252
|
+
## Development
|
|
253
|
+
|
|
254
|
+
### Project Structure
|
|
255
|
+
|
|
256
|
+
```
|
|
257
|
+
fluke_reader/
|
|
258
|
+
├── fluke_reader/ # Main library code
|
|
259
|
+
│ ├── __init__.py
|
|
260
|
+
│ ├── parsers.py # File parsers
|
|
261
|
+
│ ├── reader.py # Main reader functions
|
|
262
|
+
│ └── models.py # Data models
|
|
263
|
+
├── examples/ # Usage examples
|
|
264
|
+
├── test/ # Test files and analysis scripts
|
|
265
|
+
├── legacy/ # Legacy code and references
|
|
266
|
+
└── README.md
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
### Running Tests
|
|
270
|
+
|
|
271
|
+
```bash
|
|
272
|
+
# Run basic tests
|
|
273
|
+
python -m pytest
|
|
274
|
+
|
|
275
|
+
# Run with coverage
|
|
276
|
+
python -m pytest --cov=fluke_thermal_reader
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
## Contributing
|
|
280
|
+
|
|
281
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
282
|
+
|
|
283
|
+
**Adding support for new camera models**: If you have .is2 files from other Fluke thermal cameras, please share them so we can extend support to additional models. You can:
|
|
284
|
+
- Open an issue with sample files
|
|
285
|
+
- Submit a pull request with test data
|
|
286
|
+
- Contact the maintainers directly
|
|
287
|
+
|
|
288
|
+
## License
|
|
289
|
+
|
|
290
|
+
This project is licensed under the MIT License - see the LICENSE file for details.
|
|
291
|
+
|
|
292
|
+
## Acknowledgments
|
|
293
|
+
|
|
294
|
+
- Fluke Corporation for the thermal imaging technology
|
|
295
|
+
- The open-source community for inspiration and tools
|
|
296
|
+
|
|
297
|
+
## Changelog
|
|
298
|
+
|
|
299
|
+
### Version 1.0.0
|
|
300
|
+
- Initial release
|
|
301
|
+
- Full .is2 support
|
|
302
|
+
- Accurate temperature conversion (< 0.5°C difference from Fluke software)
|
|
303
|
+
- Metadata extraction
|
|
304
|
+
- Fusion offset correction
|
|
305
|
+
- Support for Ti300 and Ti480P cameras
|
|
306
|
+
- Ready for additional camera model support with user feedback
|
|
307
|
+
|
|
308
|
+
---
|
|
309
|
+
|
|
310
|
+
## read_is3 (Future Work)
|
|
311
|
+
|
|
312
|
+
`read_is3(file_path)` is planned for Fluke IS3 (video) files.
|
|
313
|
+
|
|
314
|
+
- Current status: Not implemented — calling it raises `NotImplementedError`.
|
|
315
|
+
- Scope: video streams (multiple thermal frames), video-level metadata.
|
|
316
|
+
- Documentation: the returned data structure will be defined once implementation starts.
|
|
317
|
+
|
|
318
|
+
If you are interested in IS3 support, please open an issue with sample files and requirements.
|
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
# Fluke Thermal Reader
|
|
2
|
+
|
|
3
|
+
A Python library for reading and analyzing Fluke thermal files (.is2 and .is3 formats).
|
|
4
|
+
|
|
5
|
+
**Package**: `fluke_thermal_reader` | **Import**: `import fluke_thermal_reader`
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- **Complete .is2 support**: Parse Fluke .is2 thermal imaging files
|
|
10
|
+
- **Accurate temperature conversion**: Convert raw thermal data to temperature values with high precision
|
|
11
|
+
- **Metadata extraction**: Extract camera information, calibration data, and image properties
|
|
12
|
+
- **Fusion offset correction**: Automatic correction for horizontal shift in thermal images
|
|
13
|
+
- **Lightweight design**: Minimal dependencies (only numpy required)
|
|
14
|
+
- **Optional image loading**: Images are returned as paths, not loaded automatically
|
|
15
|
+
- **Tested camera models**: Ti300 and Ti480P (other models supported with user feedback)
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
pip install fluke_thermal_reader
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
**Important**: package name and import are identical: `fluke_thermal_reader`.
|
|
24
|
+
|
|
25
|
+
## Quick Start
|
|
26
|
+
|
|
27
|
+
```python
|
|
28
|
+
from fluke_thermal_reader import read_is2
|
|
29
|
+
|
|
30
|
+
# Load a Fluke .is2 file
|
|
31
|
+
data = read_is2("thermal_image.is2")
|
|
32
|
+
|
|
33
|
+
# Access thermal data
|
|
34
|
+
thermal_data = data['data'] # 2D numpy array of temperatures
|
|
35
|
+
print(f"Temperature range: {thermal_data.min():.1f}°C - {thermal_data.max():.1f}°C")
|
|
36
|
+
|
|
37
|
+
# Access metadata
|
|
38
|
+
print(f"Camera: {data['CameraModel']}")
|
|
39
|
+
print(f"Image size: {data['size']}")
|
|
40
|
+
print(f"Emissivity: {data['Emissivity']}")
|
|
41
|
+
print(f"Background temperature: {data['BackgroundTemp']}°C")
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Supported File Formats
|
|
45
|
+
|
|
46
|
+
- **IS2 (images)**: Fluke thermal image format — FULLY SUPPORTED
|
|
47
|
+
- **IS3 (video)**: Fluke thermal video format — FUTURE WORK (not implemented yet)
|
|
48
|
+
|
|
49
|
+
All the examples and usage below refer to IS2 image files only.
|
|
50
|
+
|
|
51
|
+
## Image Handling
|
|
52
|
+
|
|
53
|
+
The library is designed to be lightweight and efficient. Images (thumbnails and visible photos) are handled as follows:
|
|
54
|
+
|
|
55
|
+
- **Paths only**: Images are returned as file paths, not loaded automatically
|
|
56
|
+
- **Optional loading**: You choose when and how to load images
|
|
57
|
+
- **Memory efficient**: No automatic image loading saves memory
|
|
58
|
+
- **Flexible**: Use any image library you prefer (matplotlib, PIL, opencv, etc.)
|
|
59
|
+
|
|
60
|
+
```python
|
|
61
|
+
# Images are returned as paths
|
|
62
|
+
data = read_is2("thermal_image.is2")
|
|
63
|
+
print(f"Thumbnail path: {data['thumbnail_path']}")
|
|
64
|
+
print(f"Photo path: {data['photo_path']}")
|
|
65
|
+
|
|
66
|
+
# Load images only when needed
|
|
67
|
+
if data['thumbnail_path']:
|
|
68
|
+
# Option 1: Using matplotlib
|
|
69
|
+
import matplotlib.pyplot as plt
|
|
70
|
+
thumbnail = plt.imread(data['thumbnail_path'])
|
|
71
|
+
|
|
72
|
+
# Option 2: Using PIL (lighter)
|
|
73
|
+
from PIL import Image
|
|
74
|
+
thumbnail = Image.open(data['thumbnail_path'])
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Tested Camera Models
|
|
78
|
+
|
|
79
|
+
- **Ti300**: Fully tested and supported
|
|
80
|
+
- **Ti480P**: Fully tested and supported
|
|
81
|
+
|
|
82
|
+
**Other Fluke camera models**: If you have files from other Fluke thermal cameras, please share them so we can add support for additional models.
|
|
83
|
+
|
|
84
|
+
## API Reference
|
|
85
|
+
|
|
86
|
+
### `read_is2(file_path)`
|
|
87
|
+
|
|
88
|
+
Load and parse a Fluke thermal .is2 file.
|
|
89
|
+
|
|
90
|
+
**Parameters:**
|
|
91
|
+
- `file_path` (str): Path to the .is2 file
|
|
92
|
+
|
|
93
|
+
**Returns:**
|
|
94
|
+
- `dict`: Dictionary containing thermal data and metadata
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
**Returned Data Structure (read_is2):**
|
|
98
|
+
```python
|
|
99
|
+
{
|
|
100
|
+
'data': numpy.ndarray, # 2D array of temperature values
|
|
101
|
+
'FileName': str, # Original filename
|
|
102
|
+
'CameraModel': str, # Camera model
|
|
103
|
+
'CameraSerial': str, # Camera serial number
|
|
104
|
+
'size': [width, height], # Image dimensions
|
|
105
|
+
'MinTemp': float, # Minimum temperature
|
|
106
|
+
'MaxTemp': float, # Maximum temperature
|
|
107
|
+
'AvgTemp': float, # Average temperature
|
|
108
|
+
'Emissivity': float, # Emissivity setting
|
|
109
|
+
'BackgroundTemp': float, # Background temperature
|
|
110
|
+
'CaptureDateTime': str, # Capture date and time
|
|
111
|
+
'thumbnail_path': str, # Path to thumbnail image (if available)
|
|
112
|
+
'photo_path': str, # Path to visible light image (if available)
|
|
113
|
+
}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## Examples
|
|
117
|
+
|
|
118
|
+
### Simple Usage (No External Dependencies)
|
|
119
|
+
|
|
120
|
+
```python
|
|
121
|
+
from fluke_thermal_reader import read_is2
|
|
122
|
+
import numpy as np
|
|
123
|
+
|
|
124
|
+
# Load thermal data
|
|
125
|
+
data = read_is2("thermal_image.is2")
|
|
126
|
+
|
|
127
|
+
# Basic analysis
|
|
128
|
+
temperatures = data['data']
|
|
129
|
+
print(f"Temperature range: {temperatures.min():.1f}°C - {temperatures.max():.1f}°C")
|
|
130
|
+
print(f"Average: {temperatures.mean():.1f}°C")
|
|
131
|
+
|
|
132
|
+
# Hot spot analysis
|
|
133
|
+
hot_spots = temperatures > (temperatures.mean() + 2 * temperatures.std())
|
|
134
|
+
print(f"Hot spots: {np.sum(hot_spots)} pixels")
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### Basic Usage (With Visualization)
|
|
138
|
+
|
|
139
|
+
```python
|
|
140
|
+
from fluke_thermal_reader import read_is2
|
|
141
|
+
import matplotlib.pyplot as plt
|
|
142
|
+
|
|
143
|
+
# Load thermal data
|
|
144
|
+
data = read_is2("thermal_image.is2")
|
|
145
|
+
|
|
146
|
+
# Display thermal image
|
|
147
|
+
plt.imshow(data['data'], cmap='hot')
|
|
148
|
+
plt.colorbar(label='Temperature (°C)')
|
|
149
|
+
plt.title(f'Thermal Image - {data["CameraModel"]}')
|
|
150
|
+
plt.show()
|
|
151
|
+
|
|
152
|
+
# Load images separately if needed (optional)
|
|
153
|
+
if data['thumbnail_path']:
|
|
154
|
+
# Using matplotlib (requires matplotlib)
|
|
155
|
+
thumbnail = plt.imread(data['thumbnail_path'])
|
|
156
|
+
plt.figure()
|
|
157
|
+
plt.imshow(thumbnail)
|
|
158
|
+
plt.title('Thumbnail')
|
|
159
|
+
plt.show()
|
|
160
|
+
|
|
161
|
+
# Or using PIL (lighter alternative)
|
|
162
|
+
# from PIL import Image
|
|
163
|
+
# thumbnail = Image.open(data['thumbnail_path'])
|
|
164
|
+
# thumbnail.show()
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
### Batch Processing
|
|
168
|
+
|
|
169
|
+
```python
|
|
170
|
+
import os
|
|
171
|
+
from fluke_thermal_reader import read_is2
|
|
172
|
+
|
|
173
|
+
# Process multiple files
|
|
174
|
+
for filename in os.listdir("thermal_images/"):
|
|
175
|
+
if filename.endswith(".is2"):
|
|
176
|
+
data = read_is2(f"thermal_images/{filename}")
|
|
177
|
+
print(f"Processed {filename}: {data['MinTemp']:.1f}°C - {data['MaxTemp']:.1f}°C")
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
### Temperature Analysis
|
|
181
|
+
|
|
182
|
+
```python
|
|
183
|
+
import numpy as np
|
|
184
|
+
from fluke_thermal_reader import read_is2
|
|
185
|
+
|
|
186
|
+
# Load data
|
|
187
|
+
data = read_is2("thermal_image.is2")
|
|
188
|
+
temperatures = data['data']
|
|
189
|
+
|
|
190
|
+
# Statistical analysis
|
|
191
|
+
print(f"Temperature statistics:")
|
|
192
|
+
print(f" Mean: {temperatures.mean():.1f}°C")
|
|
193
|
+
print(f" Std: {temperatures.std():.1f}°C")
|
|
194
|
+
print(f" Min: {temperatures.min():.1f}°C")
|
|
195
|
+
print(f" Max: {temperatures.max():.1f}°C")
|
|
196
|
+
|
|
197
|
+
# Find hot spots
|
|
198
|
+
hot_spots = temperatures > (temperatures.mean() + 2 * temperatures.std())
|
|
199
|
+
print(f"Hot spots: {np.sum(hot_spots)} pixels")
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
## Accuracy
|
|
203
|
+
|
|
204
|
+
The library provides highly accurate temperature readings with:
|
|
205
|
+
- **Mean difference**: < 0.5°C compared to Fluke's official software
|
|
206
|
+
- **Correlation**: > 0.999 with reference data
|
|
207
|
+
- **Precision**: 16 decimal places for temperature values
|
|
208
|
+
- **Tested on**: Ti300 and Ti480P cameras with real-world data
|
|
209
|
+
|
|
210
|
+
## Requirements
|
|
211
|
+
|
|
212
|
+
- Python 3.8+
|
|
213
|
+
- numpy>=1.20.0
|
|
214
|
+
|
|
215
|
+
**Optional dependencies for visualization:**
|
|
216
|
+
- matplotlib (for plotting thermal data)
|
|
217
|
+
- PIL/Pillow (for loading images)
|
|
218
|
+
|
|
219
|
+
## Development
|
|
220
|
+
|
|
221
|
+
### Project Structure
|
|
222
|
+
|
|
223
|
+
```
|
|
224
|
+
fluke_reader/
|
|
225
|
+
├── fluke_reader/ # Main library code
|
|
226
|
+
│ ├── __init__.py
|
|
227
|
+
│ ├── parsers.py # File parsers
|
|
228
|
+
│ ├── reader.py # Main reader functions
|
|
229
|
+
│ └── models.py # Data models
|
|
230
|
+
├── examples/ # Usage examples
|
|
231
|
+
├── test/ # Test files and analysis scripts
|
|
232
|
+
├── legacy/ # Legacy code and references
|
|
233
|
+
└── README.md
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
### Running Tests
|
|
237
|
+
|
|
238
|
+
```bash
|
|
239
|
+
# Run basic tests
|
|
240
|
+
python -m pytest
|
|
241
|
+
|
|
242
|
+
# Run with coverage
|
|
243
|
+
python -m pytest --cov=fluke_thermal_reader
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
## Contributing
|
|
247
|
+
|
|
248
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
249
|
+
|
|
250
|
+
**Adding support for new camera models**: If you have .is2 files from other Fluke thermal cameras, please share them so we can extend support to additional models. You can:
|
|
251
|
+
- Open an issue with sample files
|
|
252
|
+
- Submit a pull request with test data
|
|
253
|
+
- Contact the maintainers directly
|
|
254
|
+
|
|
255
|
+
## License
|
|
256
|
+
|
|
257
|
+
This project is licensed under the MIT License - see the LICENSE file for details.
|
|
258
|
+
|
|
259
|
+
## Acknowledgments
|
|
260
|
+
|
|
261
|
+
- Fluke Corporation for the thermal imaging technology
|
|
262
|
+
- The open-source community for inspiration and tools
|
|
263
|
+
|
|
264
|
+
## Changelog
|
|
265
|
+
|
|
266
|
+
### Version 1.0.0
|
|
267
|
+
- Initial release
|
|
268
|
+
- Full .is2 support
|
|
269
|
+
- Accurate temperature conversion (< 0.5°C difference from Fluke software)
|
|
270
|
+
- Metadata extraction
|
|
271
|
+
- Fusion offset correction
|
|
272
|
+
- Support for Ti300 and Ti480P cameras
|
|
273
|
+
- Ready for additional camera model support with user feedback
|
|
274
|
+
|
|
275
|
+
---
|
|
276
|
+
|
|
277
|
+
## read_is3 (Future Work)
|
|
278
|
+
|
|
279
|
+
`read_is3(file_path)` is planned for Fluke IS3 (video) files.
|
|
280
|
+
|
|
281
|
+
- Current status: Not implemented — calling it raises `NotImplementedError`.
|
|
282
|
+
- Scope: video streams (multiple thermal frames), video-level metadata.
|
|
283
|
+
- Documentation: the returned data structure will be defined once implementation starts.
|
|
284
|
+
|
|
285
|
+
If you are interested in IS3 support, please open an issue with sample files and requirements.
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Basic usage example for fluke_reader library.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from fluke_thermal_reader import read_is2
|
|
7
|
+
import numpy as np
|
|
8
|
+
|
|
9
|
+
# Optional: matplotlib for visualization (install with: pip install matplotlib)
|
|
10
|
+
try:
|
|
11
|
+
import matplotlib.pyplot as plt
|
|
12
|
+
HAS_MATPLOTLIB = True
|
|
13
|
+
except ImportError:
|
|
14
|
+
HAS_MATPLOTLIB = False
|
|
15
|
+
print("Note: matplotlib not available. Install with: pip install matplotlib")
|
|
16
|
+
|
|
17
|
+
def main():
|
|
18
|
+
"""Basic usage example."""
|
|
19
|
+
|
|
20
|
+
# Example file path (replace with your actual file)
|
|
21
|
+
file_path = "thermal_image.is2"
|
|
22
|
+
|
|
23
|
+
try:
|
|
24
|
+
# Load thermal data
|
|
25
|
+
print(f"Loading thermal data from {file_path}...")
|
|
26
|
+
data = read_is2(file_path)
|
|
27
|
+
|
|
28
|
+
# Display basic information
|
|
29
|
+
print(f"\n=== THERMAL IMAGE INFORMATION ===")
|
|
30
|
+
print(f"File: {data['FileName']}")
|
|
31
|
+
print(f"Camera: {data['CameraModel']} (Serial: {data['CameraSerial']})")
|
|
32
|
+
print(f"Image size: {data['size'][0]}x{data['size'][1]} pixels")
|
|
33
|
+
print(f"Capture date: {data['CaptureDateTime']}")
|
|
34
|
+
|
|
35
|
+
# Temperature information
|
|
36
|
+
temperatures = data['data']
|
|
37
|
+
print(f"\n=== TEMPERATURE INFORMATION ===")
|
|
38
|
+
print(f"Temperature range: {temperatures.min():.1f}°C - {temperatures.max():.1f}°C")
|
|
39
|
+
print(f"Average temperature: {temperatures.mean():.1f}°C")
|
|
40
|
+
print(f"Standard deviation: {temperatures.std():.1f}°C")
|
|
41
|
+
|
|
42
|
+
# Camera settings
|
|
43
|
+
print(f"\n=== CAMERA SETTINGS ===")
|
|
44
|
+
print(f"Emissivity: {data['Emissivity']}")
|
|
45
|
+
print(f"Background temperature: {data['BackgroundTemp']}°C")
|
|
46
|
+
|
|
47
|
+
# Display thermal image (if matplotlib is available)
|
|
48
|
+
if HAS_MATPLOTLIB:
|
|
49
|
+
plt.figure(figsize=(12, 8))
|
|
50
|
+
|
|
51
|
+
# Thermal image
|
|
52
|
+
plt.subplot(1, 2, 1)
|
|
53
|
+
im1 = plt.imshow(temperatures, cmap='hot', aspect='auto')
|
|
54
|
+
plt.title('Thermal Image')
|
|
55
|
+
plt.xlabel('X (pixels)')
|
|
56
|
+
plt.ylabel('Y (pixels)')
|
|
57
|
+
plt.colorbar(im1, label='Temperature (°C)')
|
|
58
|
+
|
|
59
|
+
# Temperature histogram
|
|
60
|
+
plt.subplot(1, 2, 2)
|
|
61
|
+
plt.hist(temperatures.flatten(), bins=50, alpha=0.7, edgecolor='black')
|
|
62
|
+
plt.title('Temperature Distribution')
|
|
63
|
+
plt.xlabel('Temperature (°C)')
|
|
64
|
+
plt.ylabel('Frequency')
|
|
65
|
+
plt.axvline(temperatures.mean(), color='red', linestyle='--',
|
|
66
|
+
label=f'Mean: {temperatures.mean():.1f}°C')
|
|
67
|
+
plt.legend()
|
|
68
|
+
|
|
69
|
+
plt.tight_layout()
|
|
70
|
+
plt.show()
|
|
71
|
+
else:
|
|
72
|
+
print("\n=== VISUALIZATION ===")
|
|
73
|
+
print("Install matplotlib to see thermal image visualization:")
|
|
74
|
+
print("pip install matplotlib")
|
|
75
|
+
|
|
76
|
+
# Find hot spots
|
|
77
|
+
hot_threshold = temperatures.mean() + 2 * temperatures.std()
|
|
78
|
+
hot_spots = temperatures > hot_threshold
|
|
79
|
+
hot_count = np.sum(hot_spots)
|
|
80
|
+
|
|
81
|
+
print(f"\n=== HOT SPOT ANALYSIS ===")
|
|
82
|
+
print(f"Hot spots (> {hot_threshold:.1f}°C): {hot_count} pixels")
|
|
83
|
+
print(f"Hot spot percentage: {100 * hot_count / temperatures.size:.1f}%")
|
|
84
|
+
|
|
85
|
+
if hot_count > 0:
|
|
86
|
+
hot_temps = temperatures[hot_spots]
|
|
87
|
+
print(f"Hot spot temperature range: {hot_temps.min():.1f}°C - {hot_temps.max():.1f}°C")
|
|
88
|
+
|
|
89
|
+
except FileNotFoundError:
|
|
90
|
+
print(f"Error: File {file_path} not found.")
|
|
91
|
+
print("Please replace 'thermal_image.is2' with the path to your actual .is2 file.")
|
|
92
|
+
except Exception as e:
|
|
93
|
+
print(f"Error loading thermal data: {e}")
|
|
94
|
+
|
|
95
|
+
if __name__ == "__main__":
|
|
96
|
+
main()
|