UTi712S
Extrae la matriz térmica de los BMP que guarda la cámara UNI-T UTi712S, la convierte a grados Celsius con la misma escala que usa el software de la cámara, y permite consultar cada punto en un visor.
El archivo no trae la lectura del sensor. Trae la imagen de pantalla, de 8 bits, y tres temperaturas puntuales (mínima, máxima y central). La matriz en °C es la interpolación que arma ImageFile.dll a partir de esos datos. Sirve para reproducir lo que muestra el programa oficial. No mejora la medición.
La cámara
La UTi712S guarda las capturas en BMP. El sensor es de 120×90. La pantalla es de 320×240 y el archivo sale en 240×320, el mismo recuadro girado a vertical. El rango publicado por UNI-T es de -20 °C a 400 °C, con exactitud de ±2 °C o ±2 %, el que sea mayor. La emisividad de fábrica es 0,95.
Esas tres cifras (rango, exactitud, emisividad) describen los puntos que mide la cámara. No describen el byte de cada píxel.
Instalación
Hace falta Python 3.10 o posterior. El paquete está en PyPI. Con pipx:
pipx install uti712s
Quedan dos comandos: uti712s-extract y uti712s-view.
Pillow es opcional. Sin él se escriben el JSON y los CSV, y no los PNG. El extra se llama png. En el entorno de pipx:
pipx inject uti712s pillow
El visor usa tkinter, que no se instala por pip. En Debian o Ubuntu: sudo apt install python3-tk. Tiene que estar en el mismo Python que usa pipx.
Desde el repositorio, sin pasar por PyPI:
pipx install git+https://github.com/pablogventura/uti712s.git
O, desde una copia local:
pipx install .
Uso
uti712s-extract 'images/IMG 0001.bmp'
Sin instalar, desde el repositorio:
python3 uti712s_extract.py 'images/IMG 0001.bmp'
Los archivos salen al lado del BMP:
| Archivo | Contenido |
|---|---|
IMG 0001_metadatos.json |
Temperaturas puntuales, anclas, modo y píxeles de control |
IMG 0001_intensidades.csv |
Matriz de 8 bits, una fila de imagen por línea, sin encabezado |
IMG 0001_celsius.csv |
La misma grilla en °C, ocho decimales |
IMG 0001_intensidades.png |
La matriz en gris, sin la interfaz de la cámara |
IMG 0001_termica_sin_indicadores.png |
La misma matriz coloreada con la paleta embebida |
La fila 0 es el borde de arriba. En modo 7, que es el de las capturas de images/, el orden es el del archivo.
Desde Python
read_capture lee el BMP y no escribe archivos. celsius_at calcula un punto con la escala de dos tramos. extract escribe el JSON, los CSV y, si Pillow está instalado, los PNG.
from pathlib import Path
from uti712s_extract import read_capture, extract
capture = read_capture(Path("images/IMG 0001.bmp"))
print(capture.celsius_at(119, 159))
extract(Path("images/IMG 0001.bmp"))
Visor
Abre la escena térmica sin la interfaz de la cámara y muestra la temperatura estimada de cada píxel. Usa la paleta embebida y la misma escala que el CSV. Abrir un BMP no escribe archivos.
uti712s-view
uti712s-view 'images/IMG 0001.bmp'
Sin instalar, desde el repositorio: python3 uti712s_view.py y python3 uti712s_view.py 'images/IMG 0001.bmp'.
Archivo, Abrir, carga otro BMP. La rueda del mouse sobre la imagen cambia el zoom, de 1 a 6. Cada píxel de origen sigue siendo un bloque. La rueda sobre el panel desplaza el texto, si no entra en la ventana.
El panel tiene tres bloques.
Bajo el mouse. La temperatura en °C, con un decimal, del píxel que está bajo el puntero. También muestra la columna y la fila. La columna se cuenta desde la izquierda y la fila desde arriba. El brillo es el byte de 0 a 255 guardado en el archivo. La temperatura es la estimación en °C. Si el puntero no está sobre la imagen, el bloque pide pasar el mouse por ella.
Punto fijado. Un clic deja esa lectura marcada, aunque el mouse se mueva. Otro clic, o Escape, la suelta. El punto se dibuja con un cuadrado rosa, un poco más grande que el píxel.
Mediciones de la cámara. Los tres valores del pie, la emisividad y el modo.
- Mínima y máxima: el punto más frío y el más caliente que midió la cámara. No se marcan en la imagen. Esas coordenadas a veces caen fuera, y no siempre coinciden con el byte más frío o más caliente.
- Central: la lectura del centro. El cuadrado celeste queda en ese valor. Los demás píxeles se estiman a partir de estos tres.
- Emisividad: la que tenía configurada la cámara. En estas capturas es 0,95, el valor de fábrica.
- Modo 7: la foto no se gira. Arriba es arriba.
Hay dos marcas fijas. El cuadrado celeste es el píxel de la curva. En estas capturas está en la columna 119, fila 159, y su temperatura es la central. La cruz amarilla es el centro que dibuja el software, en la columna 120, fila 160. En modo 7 son píxeles distintos. Si el mouse cae en una, el panel lo dice.
Qué hay en el BMP
Las 32 capturas de images/ miden 230995 bytes y comparten esta forma: BMP de 16 bits, 240×320, sin compresión, y un bloque extra de 77341 bytes.
El programa oficial no lee el ancho ni el alto del encabezado. Fija 240×320 y empieza la zona térmica en el offset 0x25839 (153657). Este script lee el tamaño en el encabezado y entra en bfSize + 3. En un BMP de 16 bits y 240×320, bfSize vale 153654 (54 bytes de encabezado más 153600 de píxeles), así que bfSize + 3 cae en el mismo byte. Además exige que el bloque extra mida 3 + ancho*alto + 512 + 26 y que arranque con tres ceros.
0 encabezado BMP y píxeles de 16 bits 153654 bytes
153654 00 00 00 3 bytes
153657 intensidades, 1 byte por píxel 240 * 320 = 76800
230457 paleta RGB565, 256 colores 512
230969 pie 26
230995 fin
El BMP visible es la pantalla: cruz, textos, batería, fecha y barra de color. La matriz de 8 bits es la escena térmica sin esa interfaz. El origen vertical coincide con la foto: arriba es arriba.
La paleta embebida no reproduce los píxeles del BMP. En estas capturas la coincidencia exacta es prácticamente nula. La pantalla lleva la interfaz encima y usa otro estirado de color. El PNG sin indicadores muestra la escena limpia, con la paleta del archivo, no una copia de la pantalla.
Paleta
Son 256 colores de 16 bits, little-endian, RGB565:
| Canal | Bits | Expansión que usa el software |
|---|---|---|
| Rojo | 15..11 | los 5 bits, desplazados 3 a la izquierda (máximo 248) |
| Verde | 10..5 | los 6 bits, desplazados 2 a la izquierda |
| Azul | 4..0 | los 5 bits, desplazados 3 a la izquierda |
El índice es el byte de intensidad, sin estirar al rango de la escena.
Pie de 26 bytes
Little-endian. Las temperaturas son enteros con signo, en décimas. Las coordenadas son enteros sin signo.
| Offset | Tipo | Campo |
|---|---|---|
| 0 | uint8 | Unidad. 0 es °C, 1 es °F |
| 1 | int16 | Temperatura máxima × 10 |
| 3 | int16 | Temperatura mínima × 10 |
| 5 | uint8 | Ancla caliente: intensidad que el software iguala a la máxima |
| 6 | uint8 | Ancla fría: intensidad que el software iguala a la mínima |
| 7 | int16 | Temperatura central × 10 |
| 9 | uint8 | Emisividad × 100 |
| 10 | uint8 | Modo de orientación |
| 11 | 3 bytes | El software no los lee. En estas capturas valen 0 |
| 14 | uint16 | X del mínimo |
| 16 | uint16 | Y del mínimo |
| 18 | uint16 | X del máximo |
| 20 | uint16 | Y del máximo |
| 22 | uint16 | X del centro guardado |
| 24 | uint16 | Y del centro guardado |
En las 32 capturas: unidad °C, emisividad 0,95, modo 7, ancla fría 0, ancla caliente igual al máximo de la matriz, centro guardado en (120, 160).
El byte 5 y el byte 6 juntos parecen un int16. No lo son. El software los lee por separado. Como el byte 6 es 0, ese int16 coincide con la intensidad máxima, y por eso un parseo anterior lo veía como "el máximo de la matriz" metido en un solo campo.
Modo de orientación
El modo es el byte 10. Cambia el orden de la matriz antes de convertir, y también cómo se interpretan las coordenadas de mínimo y máximo. El centro dibujado no sale del pie: el software lo fija según el modo.
| Modo | Matriz | Mínimo y máximo | Centro dibujado |
|---|---|---|---|
| 1 | salida[fila, col] = origen[alto * (ancho - col - 1) + fila] |
x' = ancho - y - 1, y' = x - 1 |
(alto/2 - 1, ancho/2 - 1) = (159, 119) |
| 2 y 5 | el buffer al revés | x' = ancho - x - 1, y' = alto - y - 1 |
(ancho/2 - 1, alto/2 - 1) = (119, 159) |
| otro, incluido el 7 | se deja como está | se dejan como están | (ancho/2, alto/2) = (120, 160) |
Las 32 capturas son modo 7. Los modos 1, 2 y 5 están implementados como en el DLL, pero este conjunto no los trae.
Las coordenadas de mínimo y máximo pueden caer fuera del raster. En estas fotos aparece x = 240 o y = 321. El software las guarda igual, sin recortarlas. A veces el marcador cae a unos pocos píxeles del extremo de intensidad, y a veces a decenas. La temperatura puntual y el byte más frío o más caliente no son el mismo dato: el byte ya pasó por el realce de contraste.
Escala a °C
El píxel de la curva no es la cruz. Después de reordenar, el software toma la intensidad Ic en (ancho/2 - 1, alto/2 - 1). En 240×320 eso es (119, 159), un píxel arriba y a la izquierda de la cruz (120, 160).
Con las temperaturas todavía en décimas:
- si la intensidad
pes mayor o igual queIc, la recta une(ancla caliente, máxima)con(Ic, central); - si es menor, une
(Ic, central)con(ancla fría, mínima).
raw = p * (Tb - Ta) / (Ib - Ia) + (Ta * Ib - Tb * Ia) / (Ib - Ia)
°C = raw / 10
(Ia, Ta) es el primer punto del tramo y (Ib, Tb) el segundo. Si el archivo está en Fahrenheit, este script pasa el resultado a Celsius con (°F - 32) / 1.8. El programa oficial, en ese caso, deja la primera matriz en la unidad del archivo y arma otra con valor * 1.8 + 32. En las capturas de images/ la unidad es °C, así que el CSV coincide con la matriz del programa.
El píxel de la curva queda, por construcción, en la temperatura central. La cruz no. En IMG 0001.bmp:
| Intensidad | °C | |
|---|---|---|
| Curva (119, 159) | 17 | 13,3 |
| Cruz (120, 160) | 16 | 12,84705882 |
| Ancla fría | 0 | 5,6 |
| Píxel más frío de esa foto | 1 | 6,05294118 |
| Ancla caliente | 253 | 21,6 |
La mínima del pie es 5,6 °C y la máxima 21,6 °C. El píxel más frío no llega a 5,6 °C porque la ancla fría está en la intensidad 0 y ese píxel vale 1.
Lo que no se puede recuperar
El sensor entrega bastante más que 8 bits. Antes de guardar, la cámara realza el contraste y estira el resultado a 240×320. Muchas temperaturas distintas caen en el mismo byte, y la tabla de ese realce no viaja en el archivo. Con tres puntos medidos se puede clavar la curva en la mínima, la máxima y el centro. Cualquier otro píxel sigue siendo una estimación, y otra curva que pase por los mismos tres puntos no está mejor medida.
El error acompaña el rango de la escena. Con menos de 2 °C de diferencia, como en IMG 0008.bmp o IMG 0031.bmp, queda por debajo de 0,1 °C. En IMG 0001.bmp la cruz ya difiere 0,45 °C de la medición central. En una escena de cientos de grados la diferencia es de decenas de grados.
IMG 0003.bmp guarda una máxima de 431,7 °C y la pantalla muestra OL. IMG 0004.bmp guarda 429,6 °C, por encima del rango de -20 °C a 400 °C publicado para esta cámara. Esos máximos están en el archivo, y la cámara no los trata como una lectura normal.
Para un punto que importe, la lectura que vale es la del puntero en la cámara.
Software oficial y otros parseos
UNI-T publica el UTi712S PC Software (junio de 2022, un Setup.exe de Windows dentro del zip). La lógica de este formato está en ImageFile.dll, en refreshParam y en el pasaje de intensidad a temperatura. Este repositorio no incluye ese instalador.
El programa oficial hace esta misma escala de dos tramos. No tiene un plano radiométrico escondido.
UNI-T Thermal Utilities documenta un BMP parecido, el de la UTi260B. No sirve para estos archivos: aquella cámara guarda BMP de 24 bits, sin los 3 bytes de relleno, y ordena primero el punto máximo. El autor describe el mismo límite en temperature_issue.md: la imagen embebida ya viene realzada, y en el postproceso solo se pueden fiar la mínima, la máxima y el centro.
Muestras
images/ tiene 32 BMP sacados de una UTi712S, de septiembre de 2020 a agosto de 2026. Todas entran en el formato de arriba, en modo 7 y en °C. Sirven para comprobar el parseo y la escala. No son una calibración.
Metadata
Release files for uti712s 0.1.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| uti712s-0.1.1.tar.gz | 15.2 kB | Without provenance | Details for uti712s-0.1.1.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| uti712s-0.1.1-py3-none-any.whl | Python 3 | none | any | Details for uti712s-0.1.1-py3-none-any.whl |
Total release size: 30.9 kB
Release files / uti712s-0.1.1.tar.gz
| Download URL | uti712s-0.1.1.tar.gz |
|---|---|
| Size | 15.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
2a0e2ebe06454a1efe9337afe80bf287984869be81041ea96bd4d93ed1186bd2
|
|
BLAKE2b-256 checksum How to use checksums |
417896c6bf5a8ee2904b445e7fb390d3b5254cd5ba7c8462e870f4d60c0be312
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.4
|
Release files / uti712s-0.1.1-py3-none-any.whl
| Download URL | uti712s-0.1.1-py3-none-any.whl |
|---|---|
| Size | 15.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
d48bbd144c6a97b94f42263ed611441ff50f0878f14e335d3932568ef30dd68c
|
|
BLAKE2b-256 checksum How to use checksums |
b4b98c822ea2eea1d31725c438024948f44a9f209f83a890366d04ca176c5887
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.4
|
Security for uti712s 0.1.1
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.