Hacktoberfest 2026: los issues que los mantenedores marcaron para octubre, abiertos y aptos para principiantes. Explorar issues de Hacktoberfest

FITS: BZERO/BSCALE are ignored and 16-bit data is read as unsigned

Abierto
#10,309 2 comentarios 0 reacciones 0 asignados Ver en GitHub

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
Tipo de issue
Error
Claridad
Bastante claro
Estado de actividad
Activo
Stack tecnológico
python
Área
backend, data

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:

  1. Physical values. Apply BZERO/BSCALE: unsigned 16-bit (BZERO=32768) stays
    I;16, signed 16-bit and signed bytes become I, and non-integer scaling becomes F.
    This matches astropy, but the mode of some existing files would change from I;16 to I.
  2. Array values, correct signedness. Read BITPIX=16 as signed (I;16BS to I, as
    the TIFF plugin does), leave the scaling to the caller and expose BZERO/BSCALE in
    im.info.
  3. 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

Primeros pasos

  1. Lee el issue completo y luego la guía de contribución del proyecto.
  2. Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
  3. Haz un fork del repositorio y trabaja en una rama.
  4. Abre un pull request que haga referencia al número del issue.

Más de python-pillow/Pillow

Todos los issues de python-pillow/Pillow

Issues similares

Más issues de Python

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.