Skip to content

Take the jpeg2000 release that carries 9/7 coefficients at float32 - #95

Merged
tannevaled merged 1 commit into
mainfrom
bump-jpeg2000-v0110
Sep 28, 2026
Merged

tannevaled merged 1 commit into
mainfrom
bump-jpeg2000-v0110

Conversation

@tannevaled

Copy link
Copy Markdown
Contributor

This changes pixels

go-images/jpeg2000 v0.11.0 carries the inverse 9/7 coefficients at float32,
which is the precision the reference carries them at — OpenJPEG reinterprets a
tile-component's OPJ_INT32* as OPJ_FLOAT32* for 9/7 and holds no second
buffer (dwt.c:157, tcd.h:210).

634 of the 3215 documents of the measured corpus render differently, by
about 0.005 levels of 255.

They move toward the judge, 2.4 to 1

The conformance aggregate does not move — mean |diff| and the
identical-pixel share are the same to four figures on four of five populations.
That is the aggregate's resolution, not the absence of an effect: it is a
percentage over hundreds of millions of pixels, and each page moved by 0.005
levels. A number that does not move is not the same as a thing that does not
move.

Asked document by document instead — for each page, which build lands
closer to poppler:

documents
v0.11.0 (float32) closer 413 67.5%
v0.10.1 (float64) closer 171 27.9%
tied 28 4.6%
unusable (one side would not render) 22

612 judged. Under a 50/50 null over the 584 untied, 292 ± 12 would be expected;
413 is ten standard deviations away. Poppler decodes JPEG 2000 through
OpenJPEG, so carrying OpenJPEG's precision carries its rounding — the direction
is what the reference predicts.

The largest move either way is 0.004691 levels of 255. Nobody will see
this; it is not a fidelity improvement anyone can point at. It is a confirmation
that the precision is the right one, bought for a large amount of memory.

The memory, which is the point

Decoding one 5868×7885 one-component scan and nothing else, so the figures are
the decoder's:

peak RSS live heap bytes/pixel
v0.10.0 917 MB 717 MB 15.5
v0.10.1 751 MB 540 MB 11.7
v0.11.0 567 MB 368 MB 7.9

99.92% of the JPEG 2000 streams in the scanned corpus use the 9/7 filter
(119 069 of 119 164, read from each codestream's COD marker), so this is on very
nearly every scanned page.

Not claimed

Speed. A float32 vector carries twice the lanes of a float64 one and
LiftStep97 is vectorised, so this may also be faster — but it was not
measured, because the machine was carrying two system daemons at 132% and 86% of
a core. A timing taken under that says more about the daemons than the code.

The module in the cache was diffed against the tree these figures were measured
on.

🤖 Generated with Claude Code

THIS CHANGES PIXELS. 634 of the 3215 documents of the measured corpus render
differently, by about 0.005 levels of 255.

They move TOWARD the judge. Asked document by document -- for each page, which
build lands closer to poppler -- float32 is closer on 413 of 612 and further on
171, with 28 tied. Under a 50/50 null over the 584 untied, 292 +/- 12 would be
expected. That is what the reference predicts: poppler decodes JPEG 2000
through OpenJPEG, which works in float32, so carrying its precision carries its
rounding.

The conformance aggregate does NOT move: mean |diff| and the identical-pixel
share are the same to four figures. That is the aggregate's resolution, not the
absence of an effect -- it is a percentage over hundreds of millions of pixels
and the change moves each page by 0.005 levels. The per-document count is what
can see it.

Decoding one 5868x7885 one-component scan alone, peak RSS falls 751 -> 567 MB
and the live heap 540 -> 368 MB.
@tannevaled
tannevaled merged commit 93e193f into main Sep 28, 2026
9 checks passed
@tannevaled
tannevaled deleted the bump-jpeg2000-v0110 branch September 28, 2026 23:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant