TIFF parser rejects bilevel TIFFs without a BitsPerSample tag (TIFF 6.0 default is 1), e.g. 1-bit TIFFs written by Pillow

Ouverte
#66 1 commentaire 0 réactions 0 personnes assignées Voir sur GitHub

Personne n'a encore pris cette issue.

Évaluation

Difficulté
3/5
Temps estimé
1-2 jours
Accessibilité débutants
72/100
Type d'issue
Bug
Clarté
Clairement spécifiée
Activité
Active
Stack technique
cpp, python

Piste de recherche

Start by reading src/parsers/tiff.cpp, especially ensureParsedIfd near line 652, and compare its handling of missing BitsPerSample with the existing Grayscale_MissingSPP test and the libtiff extension’s defaulted tag read. Add parser and Python regression coverage for Pillow’s uncompressed 1-bit TIFF without that tag, alongside the explicit-tag case. Done means the missing-tag file parses and decodes to the same 5×8, single-channel, 1-bit image as the explicit-tag file; check the GPU decoder separately if available.

Rédigé par le modèle d'indexation à partir du texte de l'issue.

Description

Version

0.10.0 (wheel nvidia-nvimgcodec-cu12==0.10.0.9; also main at 77a7d15)

Describe the bug.

A 1-bit TIFF that has no BitsPerSample tag cannot be opened:

[ERROR] [tiff_parser] Couldn't read TIFF image required fields - missing: BitsPerSample
RuntimeError: Failed to get image info

TIFF 6.0 gives BitsPerSample a default of 1 (Section 8), and it is not among the required fields for bilevel images (Section 3). Pillow writes uncompressed mode "1" images without the tag. Pillow and OpenCV read these files, and libtiff returns the default through TIFFGetFieldDefaulted.

Expected: the file decodes like the same image saved with an explicit BitsPerSample = 1, which works (5x8, 1 channel, precision 1, values 0/255).

The check is in src/parsers/tiff.cpp (ensureParsedIfd, line 652 on main). SamplesPerPixel already gets its default for grayscale files (test Grayscale_MissingSPP), and the libtiff extension reads BitsPerSample with TIFFGetFieldDefaulted.

Minimum reproducible example
cat > repro.py <<'PY'
import io
import numpy as np
from PIL import Image
from nvidia import nvimgcodec

print("nvimgcodec", nvimgcodec.__version__)
bits = np.arange(40).reshape(5, 8) % 3 == 0
pil_image = Image.fromarray(bits)  # mode "1"

decoder = nvimgcodec.Decoder(
    device_id=nvimgcodec.NVIMGCODEC_DEVICE_CPU_ONLY,
    backends=[nvimgcodec.Backend(nvimgcodec.BackendKind.CPU_ONLY)],
)
params = nvimgcodec.DecodeParams(color_spec=nvimgcodec.ColorSpec.UNCHANGED)

# "raw" is Pillow's own writer (no BitsPerSample tag); "tiff_lzw" goes through libtiff (tag present).
for compression in ("tiff_lzw", "raw"):
    buf = io.BytesIO()
    pil_image.save(buf, "TIFF", compression=compression)
    data = buf.getvalue()
    has_tag = 258 in Image.open(io.BytesIO(data)).tag_v2
    print(f"--- compression={compression}, BitsPerSample tag present: {has_tag}")
    try:
        code_stream = nvimgcodec.CodeStream(data)
        print("parsed:", code_stream.height, code_stream.width, code_stream.num_channels,
              code_stream.dtype, "precision", code_stream.precision)
        image = decoder.decode(code_stream, params=params)
        decoded = None if image is None else np.asarray(image.cpu())[:, :, 0]
        print("decoded matches Pillow:",
              decoded is not None and np.array_equal(decoded, bits.astype(np.uint8) * 255))
    except Exception as e:
        print("ERROR:", type(e).__name__, e)
PY
python repro.py
Environment details
tools/print_env.sh was not run. Environment used for the repro:
x86_64 Linux, Ubuntu 24.04, Python 3.12
nvidia-nvimgcodec-cu12 0.10.0.9, Pillow 13.0.0, numpy 2.5.4
CPU-only backend, no GPU visible (CUDA_VISIBLE_DEVICES empty)
Also reproduced with a from-source build of main 77a7d15 (CUDA toolkit 12.9, GCC 14.4, libtiff 4.7.2)
Relevant log output
[ERROR] [tiff_parser] Couldn't read TIFF image required fields - missing: BitsPerSample (IFD at offset 8, entry_count=8)
nvimgcodec 0.10.0
--- compression=tiff_lzw, BitsPerSample tag present: True
parsed: 5 8 1 uint8 precision 1
decoded matches Pillow: True
--- compression=raw, BitsPerSample tag present: False
ERROR: RuntimeError Failed to get image info
Other/Misc.

The error message calls BitsPerSample a required field, so this check may be deliberate. If it is not, a small local parser change is available: use 1 when the tag is absent, as libtiff does. It comes with a parser test and a Python test. Two questions for you:

  • Should the default apply to every photometric type, as in libtiff, or only to single-channel images?
  • The change was not tested with the nvTIFF GPU decoder, because that extension was not in the local build. The CPU path (libtiff extension) decodes the file correctly.
Check for duplicates
  • I have searched the open bugs/issues and have found no duplicates for this bug report

This was found with an AI coding assistant (Claude Code, claude-fable-5-2), which also wrote and ran the repro above and drafted this report.

Langage dominant
Jupyter Notebook
Étoiles
164
Forks
26
Merge moyen
27 min
PR mergées (30 j)
1

Préparer son environnement

Par où commencer

  1. Lisez l'issue en entier, puis le guide de contribution du projet.
  2. Signalez en commentaire que vous la prenez — cela évite que deux personnes fassent le même travail.
  3. Forkez le dépôt et travaillez sur une branche.
  4. Ouvrez une pull request qui référence le numéro de l'issue.

Autres issues de NVIDIA/nvImageCodec

Toutes les issues de NVIDIA/nvImageCodec

Issues similaires

Plus d'issues Backend & API Design

Recevez les nouvelles issues par e-mail

Un résumé court des issues GitHub adaptées aux débutants.