PDF: an explicit stop_frame drops the last frame, and stop_frame=0 fails with "No image found"
Actions before raising this issue
- I searched the existing issues and did not find anything similar.
- I read/searched the docs
Steps to Reproduce
PdfReader can be exercised directly from the released image, so this needs no running CVAT stack:
docker run --rm -i --entrypoint python3 cvat/server:v2.75.0 - <<'EOF'
import os, django, tempfile
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "cvat.settings.testing")
django.setup()
import numpy as np
from pathlib import Path
from PIL import Image
from cvat.apps.engine.media_extractors import PdfReader
src = Path(tempfile.mkdtemp()) / "p.pdf"
imgs = [Image.fromarray(np.ones((50, 100, 3), dtype=np.uint8)) for _ in range(10)]
imgs[0].save(src, "pdf", save_all=True, resolution=200, append_images=imgs[1:])
for stop, expected in [(None, 10), (4, 5), (1, 2), (0, 1)]:
work = Path(tempfile.mkdtemp()) / "p.pdf"
work.write_bytes(src.read_bytes())
try:
got = len(list(PdfReader([work], stop=stop).frame_range))
except Exception as e:
got = f"{type(e).__name__}: {e}"
print(f"stop_frame={str(stop):>5} expected={expected:>2} got={got}")
EOFOutput on a 10-page PDF:
stop_frame= None expected=10 got=10
stop_frame= 4 expected= 5 got=4
stop_frame= 1 expected= 2 got=1
stop_frame= 0 expected= 1 got=Exception: No image foundThrough the REST API this corresponds to POST /api/tasks/{id}/data with a PDF and an explicit
stop_frame. To be clear about what I actually ran: I verified the extractor directly as above, and
traced the request path by reading views.py/task.py rather than executing it, so the API-level
symptom is inferred rather than observed.
Expected Behavior
stop_frame is documented as "Last frame index"
(serializers.py#L2716),
and ImageListReader treats it as a 0-based inclusive index
(media_extractors.py#L308-L311).
So stop_frame=4 should yield 5 frames (0..4), and stop_frame=0 should yield 1 frame.
Instead every explicit value yields one frame fewer, with no error raised, and stop_frame=0 aborts
with No image found, which points the user at their file rather than at the frame range.
Possible Solution
PdfReader passes stop straight through to pdf2image's last_page
(media_extractors.py#L463-L470),
but the two use different conventions:
- CVAT's
stop: 0-based inclusive frame index pdf2image'slast_page: 1-based inclusive page number
pdf2image 1.14.0 additionally returns [] when first_page > last_page, so last_page=0 converts
nothing and ImageListReader then raises No image found
(media_extractors.py#L306).
Converting at the call site should cover both, keeping 0 distinct from "unset" the way
views.py#L1449-L1452
already does for the omitted case.
Context
Creating a task from a PDF while restricting the frame range.
The silent case is the one that concerns me more than the exception: the task is created successfully and simply contains one frame fewer than requested, with nothing in the API response or the UI indicating that anything was truncated.
Environment
- CVAT v2.75.0 (2a8340fb10ac85f45fd0eef18aaf3f814178cb04);
same code on develop @ 1d0c39576
- Image: official cvat/server:v2.75.0
- pdf2image 1.14.0 (as pinned in cvat/requirements/base.in)
- Docker 29.x, Ubuntu, x86_64I'd be glad to put together a fix and a regression test if you agree this is worth changing.
Source: cvat-ai/cvat