FITS: BZERO/BSCALE are ignored and 16-bit data is read as unsigned
Los mantenedores suelen responder en 1 día
@karandhaodiyal28-hash ya está trabajando en esto.
Desde el 25/10/2026.
- #10312 de @karandhaodiyal28-hash — abierto
Evaluación
- Dificultad
- 4/5
- Tiempo estimado
- 3-5 días
- Aptitud para principiantes
- 45/100
Línea de trabajo
Start by locating the FITS plugin code that unpacks BITPIX=16 data and reads header cards; the issue does not name files or tests. Compare its handling of signed values and BZERO/BSCALE with the three behavior options in the issue, then check how the chosen behavior affects existing image modes. Done means the maintainers’ intended semantics are implemented and covered for signed 16-bit, BZERO/BSCALE scaling, and the unchanged 32-bit case.
Escrito por el modelo de indexación a partir del texto del issue.
Descripción
What did you do?
Opened small FITS images whose headers use BZERO/BSCALE, plus one with signed 16-bit
data, and compared the pixel values with astropy. The files are built in memory, so the
script has no data dependencies:
import io, struct
from PIL import Image
def fits(bitpix, values, extra=()):
cards = ["SIMPLE = T", "BITPIX = %20d" % bitpix,
"NAXIS = 2", "NAXIS1 = %20d" % len(values),
"NAXIS2 = 1", *extra, "END"]
header = b"".join(c.ljust(80).encode() for c in cards)
header += b" " * (-len(header) % 2880)
data = struct.pack(">%d%s" % (len(values), {8: "B", 16: "h", 32: "i"}[bitpix]), *values)
return header + data + b"\0" * (-len(data) % 2880)
unsigned16 = fits(16, [-32768, -32767, -1, 0, 32767], ["BZERO = 32768"])
signed16 = fits(16, [-32768, -1, 0, 1, 32767])
for blob in (unsigned16, signed16):
with Image.open(io.BytesIO(blob)) as im:
print(im.mode, [im.getpixel((x, 0)) for x in range(im.width)])
What did you expect to happen?
FITS integers are signed big-endian, and the values an application should see are
BZERO + BSCALE * array value (FITS Standard 4.0, Sect. 4.4.2.5, Eq. 3). Unsigned 16-bit
data, which most cameras produce, is stored as signed integers with BZERO = 32768
(Sect. 5.2.5, Table 11). astropy 8.0.2 (astropy.io.fits.getdata) gives:
| file | astropy |
|---|---|
| BITPIX=16, BZERO=32768 | [0, 1, 32767, 32768, 65535] |
| BITPIX=16, no BZERO | [-32768, -1, 0, 1, 32767] |
| BITPIX=8, BZERO=-128 | [-128, -127, -1, 0, 127] |
| BITPIX=16, BSCALE=0.5, BZERO=100 | [99.0, 100.0, 101.0, 150.0, 600.0] |
| BITPIX=32, no BZERO | [-2147483648, -1, 0, 1, 2147483647] |
What actually happened?
| file | Pillow 13.0.0 (also main 923a595) |
|---|---|
| BITPIX=16, BZERO=32768 | I;16 [32768, 32769, 65535, 0, 32767] |
| BITPIX=16, no BZERO | I;16 [32768, 65535, 0, 1, 32767] |
| BITPIX=8, BZERO=-128 | L [0, 1, 127, 128, 255] |
| BITPIX=16, BSCALE=0.5, BZERO=100 | I;16 [65534, 0, 2, 100, 1000] |
| BITPIX=32, no BZERO | I [-2147483648, -1, 0, 1, 2147483647] (correct) |
16-bit data is unpacked with I;16B, i.e. as unsigned, and BZERO/BSCALE are never
read. For the common unsigned convention every pixel comes out with its top bit flipped,
so a 16-bit camera frame looks like a solarised image. Signed 16-bit data without BZERO
wraps negative values to large positive ones. 32-bit data is already read as signed
(I;32B).
Question for maintainers
Before writing a PR I'd like to know which behaviour you want:
- Physical values. Apply
BZERO/BSCALE: unsigned 16-bit (BZERO=32768) stays
I;16, signed 16-bit and signed bytes becomeI, and non-integer scaling becomesF.
This matches astropy, but the mode of some existing files would change fromI;16toI. - Array values, correct signedness. Read BITPIX=16 as signed (
I;16BStoI, as
the TIFF plugin does), leave the scaling to the caller and exposeBZERO/BSCALEin
im.info. - Keep the raw reading as it is, and document that FITS integers are returned as
unsigned array values without scaling.
For option 1 with BZERO=32768, I don't see an existing rawmode that flips the sign bit
into I;16. It would need a small unpacker or a post-processing step in the plugin, so
tell me if you'd rather avoid that.
What are your OS, Python and Pillow versions?
- OS: Ubuntu 24.04 (x86_64, Docker)
- Python: 3.12.3
- Pillow: 13.0.0 from PyPI, and main at 923a595 built from source
- Lenguaje dominante
- Python
- Estrellas
- 13.9k
- Forks
- 2.6k
- Merge medio
- 1 d 6 h
- PR fusionados (30 d)
- 159
Preparar el entorno
- Sin Dockerfile ni archivo de Docker Compose
- Tiene una plantilla de pull request
- Leer la guía de contribución
Primeros pasos
- Lee el issue completo y luego la guía de contribución del proyecto.
- Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
- Haz un fork del repositorio y trabaja en una rama.
- Abre un pull request que haga referencia al número del issue.
Más de python-pillow/Pillow
-
Warn that pre-applying arabic_reshaper + python-bidi corrupts text on a Raqm buildPosiblemente ocupada @Syamjith-NK la tomó hace 59 días. AbiertoDocumentation Font Rendering PNG Third Party Framework
Dificultad 2/5 1-3 horas Aptitud para principiantes 78/100
python-pillow/Pillow#9907 · 2 comentarios ·
Los mantenedores suelen responder en 1 día
-
Image.putpixel incorrectly labels supported arguments in type annotationsPosiblemente ocupada @radarhere la tomó hace 187 días. AbiertoPNG
Dificultad 2/5 1-3 horas Aptitud para principiantes 70/100
python-pillow/Pillow#9584 · 1 comentario ·
Los mantenedores suelen responder en 1 día
-
cp316 drops required macro for Pyllow to compilePosiblemente ocupada @lazerg la tomó hoy. Abierto
Dificultad 4/5 3-5 días Aptitud para principiantes 48/100
python-pillow/Pillow#10323 · 2 comentarios ·
Los mantenedores suelen responder en 1 día
-
Dificultad 3/5 1-2 días Aptitud para principiantes 54/100
python-pillow/Pillow#10317 · 1 comentario ·
Los mantenedores suelen responder en 1 día
-
Wheels for Android?Posiblemente ocupada @mhsmith la tomó hace 9 días. Abierto
Dificultad 4/5 3-5 días Aptitud para principiantes 48/100
python-pillow/Pillow#10311 · 1 comentario ·
Los mantenedores suelen responder en 1 día
Todos los issues de python-pillow/Pillow
Issues similares
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 67/100
-
query rewriting: prompt says "Language for `query` field: None" when language detection failsAbierto
Dificultad 2/5 1-3 horas Aptitud para principiantes 78/100
Los mantenedores suelen responder en 1 día
-
ocr_data_to_elements() raises AttributeError when a layout element is a ListPosiblemente ocupada @Eapp1e la tomó hoy. Abierto
Dificultad 2/5 1-3 horas Aptitud para principiantes 79/100
Unstructured-IO/unstructured#4616 ·
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 65/100
Los mantenedores suelen responder en 1 día
-
area:codegen backend:metal bug direction:output
Dificultad 2/5 1-3 horas Aptitud para principiantes 79/100
Los mantenedores suelen responder en 1 día