Compare today's RGB8 file with the 12-bit file at the same quality setting in the 12-bit report
This commit is contained in:
parent
a723646928
commit
1edca07293
|
|
@ -470,5 +470,43 @@
|
|||
"rms_avg": 6.01
|
||||
}
|
||||
}
|
||||
],
|
||||
"same_setting": [
|
||||
{
|
||||
"source": "camera RGB8",
|
||||
"distance": 0.8,
|
||||
"effort": 4,
|
||||
"size_mb": 0.92,
|
||||
"dark (< 300 DN)": {
|
||||
"rms_px": 18.01,
|
||||
"rms_avg": 2.18
|
||||
},
|
||||
"mid (300-1000 DN)": {
|
||||
"rms_px": 21.4,
|
||||
"rms_avg": 2.25
|
||||
},
|
||||
"bright (>= 1000 DN)": {
|
||||
"rms_px": 34.3,
|
||||
"rms_avg": 2.82
|
||||
}
|
||||
},
|
||||
{
|
||||
"source": "BayerRG12p",
|
||||
"distance": 0.8,
|
||||
"effort": 4,
|
||||
"size_mb": 1.22,
|
||||
"dark (< 300 DN)": {
|
||||
"rms_px": 16.26,
|
||||
"rms_avg": 1.22
|
||||
},
|
||||
"mid (300-1000 DN)": {
|
||||
"rms_px": 23.57,
|
||||
"rms_avg": 1.9
|
||||
},
|
||||
"bright (>= 1000 DN)": {
|
||||
"rms_px": 42.42,
|
||||
"rms_avg": 4.35
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 2.1 MiB |
Binary file not shown.
|
After Width: | Height: | Size: 152 KiB |
|
|
@ -824,12 +824,55 @@ consequence, on this scene the 12-bit file at distance 1.0 is 46% larger than th
|
|||
frames are in any case far smaller than the 2.65 MB of the daylight frame used for the storage
|
||||
estimate (Section 3.12).
|
||||
|
||||
**Today's format against `BayerRG12p` at the same setting.** To answer directly whether a reader
|
||||
would see a difference between today's files and 12-bit files, both frames were saved with the
|
||||
same JPEG XL setting, today's distance 0.8 and effort 4. The two frames are separate captures, so
|
||||
each file is compared with its own uncompressed original rather than with the other (Table 17e,
|
||||
Figures 13g and 13h).
|
||||
|
||||
***Table 17e.** The camera's RGB8 frame and the `BayerRG12p` frame, both saved as JPEG XL at
|
||||
distance 0.8, effort 4: file size (MB) and RMS error (DN, 12-bit scale; RGB8 values × 16) against
|
||||
each file's own uncompressed frame, per brightness region as in Table 17d. Per pixel: the error at
|
||||
each pixel. Local average: the error after averaging over about 200 pixels.*
|
||||
|
||||
| File (d0.8, effort 4) | Size (MB) | Per pixel, dark / mid / bright (DN) | Local average, dark / mid / bright (DN) |
|
||||
|---|---|---|---|
|
||||
| Camera RGB8 (today) | 0.92 | 18.0 / 21.4 / 34.3 | 2.2 / 2.3 / 2.8 |
|
||||
| `BayerRG12p`, 12-bit file | 1.22 | 16.3 / 23.6 / 42.4 | 1.2 / 1.9 / 4.4 |
|
||||
|
||||

|
||||
|
||||
***Figure 13g.** The dark crop of Figure 13d from the camera's RGB8 frame and from the `BayerRG12p`
|
||||
frame, each uncompressed and saved at distance 0.8, effort 4 (the same setting for both). Rows: as
|
||||
stored (×1, as a viewer shows the linear values), brightened by 2 stops (×4) and by 4 stops (×16).
|
||||
The two frames are separate captures; branches moved between them.*
|
||||
|
||||

|
||||
|
||||
***Figure 13h.** The middle 200 × 200 pixels of the two saved crops of Figure 13g at +4 stops,
|
||||
enlarged 2 times (each pixel shown as 2 × 2 screen pixels): camera RGB8 (left) and `BayerRG12p`
|
||||
(right), both at distance 0.8, effort 4.*
|
||||
|
||||
As stored, the shaded forest is almost black and the two files cannot be told apart; at +2 stops the
|
||||
RGB8 file is slightly flatter. At +4 stops the difference is clear: the RGB8 file breaks the forest
|
||||
into flat blocks and loses the thin branches, while the 12-bit file stays close to its uncompressed
|
||||
frame (Figure 13h). In the dark region the 12-bit file keeps the local brightness 1.8 times more
|
||||
accurately (1.2 against 2.2 DN) and in the mid-tones 1.2 times; in the bright region the RGB8 file
|
||||
is 1.5 times more accurate (2.8 against 4.4 DN), the same pattern as in Table 17d. At the same
|
||||
setting the 12-bit file is 33% larger (1.22 against 0.92 MB); at distance 1.0 it is the same size as
|
||||
today's file and still free of blocks (Figure 13f d). The difference therefore shows wherever the
|
||||
shadows are brightened: a viewer applying a display curve, an operator raising the brightness, or a
|
||||
detection step stretching the contrast of dark terrain.
|
||||
|
||||
In summary, for this dark scene: (1) the four extra bits themselves change little after saving:
|
||||
1.6 against 1.8 DN of local-brightness error in the dark at distance 1.0, and 0.6 DN when 8-bit
|
||||
values are kept losslessly; (2) saving as a 12-bit file keeps about 2.3 times more accurate shadow
|
||||
brightness than saving as an 8-bit file at the same distance, and visibly more shadow detail than
|
||||
today's RGB8 file at the same size; (3) per pixel, rounding to 8 bits raises the noise in the dark
|
||||
by 10–26%, which matters only for lossless or near-lossless files.
|
||||
by 10–26%, which matters only for lossless or near-lossless files; (4) with the same JPEG XL setting
|
||||
(distance 0.8, effort 4), today's RGB8 file and the 12-bit file look the same as stored, but at
|
||||
+4 stops the RGB8 shadows break into blocks while the 12-bit file keeps the forest detail and 1.8
|
||||
times more accurate shadow brightness, in a 33% larger file.
|
||||
|
||||
#### 3.9.3 Where 12 bits matter
|
||||
|
||||
|
|
@ -959,7 +1002,7 @@ The towers have 11–22 GB of free storage, so images must leave the tower conti
|
|||
| Processing | Unpack + colour conversion 38 ms (IOTA) / 50 ms (Delta); a 12-bit save 518 / 820 ms, slightly faster than today's RGB8 save (567 / 872 ms). | 3.5 |
|
||||
| Computers | The Delta takes 1.6 times as long on the identical frame. | 3.5 |
|
||||
| Quality | d1.0: 2.65 MB, SSIM 0.973. Raw planes keep sensor values much closer (47 vs 39 dB at 6 MB). | 3.7 |
|
||||
| 12 bits | In daylight, rounding to 8 bits adds at most 1.5% to the noise, and compression at d1.0 adds 13 times more error than rounding. In a dark, high-contrast scene (Stäffelsberg) rounding adds 10–26% noise in the shadows; at d1.0 an 8-bit file loses shadow detail and 2.3 times more shadow brightness than a 12-bit file, mostly because of the file's declared bit depth, not the extra bits. Today's RGB8 file shows the same loss. | 3.9 |
|
||||
| 12 bits | In daylight, rounding to 8 bits adds at most 1.5% to the noise, and compression at d1.0 adds 13 times more error than rounding. In a dark, high-contrast scene (Stäffelsberg) rounding adds 10–26% noise in the shadows; at d1.0 an 8-bit file loses shadow detail and 2.3 times more shadow brightness than a 12-bit file, mostly because of the file's declared bit depth, not the extra bits. Today's RGB8 file shows the same loss: saved with the same setting (d0.8 e4) as a 12-bit file, it looks the same as stored but is blocky at +4 stops, with 1.8 times the shadow-brightness error, while the 12-bit file is 33% larger. | 3.9 |
|
||||
| Throughput | IOTA keeps up with a frame every 0.7 s with one worker; the Delta needs two. | 3.10 |
|
||||
| Heat | One frame every 2 s: IOTA 53 °C / 5.1 W, Delta 66 °C / 5.7 W. | 3.11 |
|
||||
| Storage | 12-bit colour d1.0: 2.7 MB per frame, below today's 3.3–4.0 MB. | 3.12 |
|
||||
|
|
|
|||
|
|
@ -170,6 +170,11 @@ have 11–22 GB free, so images must leave the tower continuously.
|
|||
0.94 MB: the effect is libjxl spending bits by declared bit depth, not the four extra bits. Sizes
|
||||
on this scene at d1.0: 12-bit 0.92 MB, 8-bit 0.63 MB (+46%; equal in daylight). Implication: if
|
||||
BayerRG8 were ever used, save its colour as a 12/16-bit JPEG XL.
|
||||
- Same setting for both (user request, Table 17e, Figures 13g/13h): camera RGB8 and BayerRG12p
|
||||
both at d0.8 e4, each scored against its own uncompressed frame (separate captures). As stored
|
||||
(linear, near black) no visible difference; at +4 stops RGB8 is blocky, 12-bit keeps detail.
|
||||
Local-average error dark/mid/bright: RGB8 2.2/2.3/2.8, 12-bit 1.2/1.9/4.4 DN; sizes 0.92 vs
|
||||
1.22 MB (+33%).
|
||||
- Not yet tested: real smoke; night at high gain; the tower's own de-Bayer on a dark frame.
|
||||
- 12-bit vs 8-bit raw costs little in saving (same size, ~10% more time) but the camera runs at
|
||||
9.86 vs 14.75 fps, ~1 s slower settling in daylight. The main gain over today is frame rate, not
|
||||
|
|
@ -205,7 +210,7 @@ mosaic vs raw planes, Figures 1a/1b, Table 1; 1.3 earlier work); 2 Materials and
|
|||
Tables 2–8, Figure 2); 3 Results (3.1 frame rate, 3.2 auto-exposure, 3.3 keep rules, 3.4 white
|
||||
balance, 3.5 processing per step, 3.6 effort and threads, 3.7 quality setting, 3.8 example images,
|
||||
3.9 information content of 12-bit data (3.9.1 daylight, 3.9.2 Stäffelsberg dark scene, 3.9.3
|
||||
summary; Figures 12, 13a–13f, Tables 17a–17d), 3.10 throughput, 3.11 heat,
|
||||
summary; Figures 12, 13a–13h, Tables 17a–17e), 3.10 throughput, 3.11 heat,
|
||||
3.12 storage; Figures 3–17, Tables 9–20); 4 Discussion (summary Table 21, pace Table 22,
|
||||
limitations); 5 Conclusions and six recommendations; References [1]–[7]; Appendix A raw data and
|
||||
tools; Appendix B per-tower processing times. Figures live in `docs/images/raw12/`.
|
||||
|
|
|
|||
|
|
@ -17,6 +17,9 @@ Writes to <out_dir>:
|
|||
dark_compressed.png dark crop after JPEG XL, +4 stops: today's camera RGB8 file (d0.8 e4),
|
||||
and at d1.0 the 8-bit file, the 8-bit values in a 12-bit file, 12-bit
|
||||
mid_crop.png mid-tone crop, 8-bit / 12-bit, contrast x4 around its mean (control)
|
||||
same_setting.png dark crop, camera RGB8 and 12-bit, uncompressed and both at d0.8 e4,
|
||||
shown x1, +2 and +4 stops
|
||||
same_setting_zoom.png the middle 200 x 200 pixels of the two d0.8 e4 crops at +4 stops, 2x
|
||||
results.json noise, levels, sizes and errors per brightness region
|
||||
"""
|
||||
import json
|
||||
|
|
@ -158,10 +161,25 @@ for name, img, bps in SOURCES:
|
|||
rms_avg=round(float(np.sqrt((e_av[m] ** 2).mean())), 2))
|
||||
errors.append(row)
|
||||
del a, e_px, e_av
|
||||
# Today's save: the camera's RGB8 at distance 0.8, effort 4.
|
||||
data = imagecodecs.jpegxl_encode(cam8, effort=4, distance=0.8, bitspersample=8)
|
||||
size[("camera RGB8 today e4", 0.8)] = len(data) / 1e6
|
||||
dark_dec[("camera RGB8 today", 0.8)] = crop(to12(imagecodecs.jpegxl_decode(data)), dark_xy).copy()
|
||||
# Both formats with today's setting (distance 0.8, effort 4). The two frames are separate captures,
|
||||
# so each is scored against its own uncompressed original.
|
||||
same_setting, same_crops = [], {}
|
||||
for name, img, bps in (("camera RGB8", cam8, 8), ("BayerRG12p", c12, 12)):
|
||||
data = imagecodecs.jpegxl_encode(np.ascontiguousarray(img), effort=4, distance=0.8, bitspersample=bps)
|
||||
own = to12(img)
|
||||
a = to12(imagecodecs.jpegxl_decode(data))
|
||||
same_crops[name] = (crop(own, dark_xy).copy(), crop(a, dark_xy).copy())
|
||||
e_px = a - own
|
||||
e_av = cv2.GaussianBlur(a, (0, 0), 4) - cv2.GaussianBlur(own, (0, 0), 4)
|
||||
row = dict(source=name, distance=0.8, effort=4, size_mb=round(len(data) / 1e6, 2))
|
||||
for rn, m in REGIONS.items():
|
||||
row[rn] = dict(rms_px=round(float(np.sqrt((e_px[m] ** 2).mean())), 2),
|
||||
rms_avg=round(float(np.sqrt((e_av[m] ** 2).mean())), 2))
|
||||
same_setting.append(row)
|
||||
print(json.dumps(row), flush=True)
|
||||
del own, a, e_px, e_av
|
||||
size[("camera RGB8 today e4", 0.8)] = same_setting[0]["size_mb"]
|
||||
dark_dec[("camera RGB8 today", 0.8)] = same_crops["camera RGB8"][1]
|
||||
|
||||
# --- crops ---------------------------------------------------------------------------------
|
||||
def upright(a):
|
||||
|
|
@ -198,6 +216,16 @@ grid([[upright(view(dark_dec[k], 4)) for k in row] for row in keys], None, None,
|
|||
os.path.join(out, "dark_compressed.png"),
|
||||
titles=[["(a) camera RGB8 file, d0.8 e4 (today)", "(b) 8-bit file, d1.0"],
|
||||
["(c) 8-bit values in a 12-bit file, d1.0", "(d) 12-bit file, d1.0"]])
|
||||
tiles = [same_crops["camera RGB8"][0], same_crops["camera RGB8"][1],
|
||||
same_crops["BayerRG12p"][0], same_crops["BayerRG12p"][1]]
|
||||
grid([[upright(view(a, s)) for a in tiles] for s in (0, 2, 4)],
|
||||
["camera RGB8, uncompressed", "camera RGB8, d0.8 e4", "BayerRG12p, uncompressed", "BayerRG12p, d0.8 e4"],
|
||||
["as stored (×1)", "+2 stops (×4)", "+4 stops (×16)"], os.path.join(out, "same_setting.png"))
|
||||
z = slice(100, 300)
|
||||
grid([[np.kron(upright(view(same_crops[n][1], 4))[z, z], np.ones((2, 2, 1), np.uint8))
|
||||
for n in ("camera RGB8", "BayerRG12p")]],
|
||||
["camera RGB8, d0.8 e4", "BayerRG12p, d0.8 e4"], ["+4 stops (×16), 2× zoom"],
|
||||
os.path.join(out, "same_setting_zoom.png"))
|
||||
mid_mean = crop(ref, mid_xy).mean(axis=(0, 1)) # per channel
|
||||
grid([[upright(np.clip((crop(a, mid_xy) - mid_mean) * 4 / 16 + 128, 0, 255).astype(np.uint8))
|
||||
for a in (to12(c8), ref)]],
|
||||
|
|
@ -230,6 +258,7 @@ res = dict(
|
|||
mean_green_12bit=round(float(crop(ref, mid_xy)[..., 1].mean()), 1)),
|
||||
sizes_mb={f"{n} d{d}": round(v, 2) for (n, d), v in size.items()},
|
||||
errors=errors,
|
||||
same_setting=same_setting,
|
||||
)
|
||||
json.dump(res, open(os.path.join(out, "results.json"), "w"), indent=1)
|
||||
print(json.dumps(res, indent=1))
|
||||
|
|
|
|||
Loading…
Reference in New Issue