Light-box protocol: T20 wyze-t20x-jxf23, 2026-11-08
OpenIMP #230 light-box test, generated 2026-11-08 12:13 by lightbox_report.py from run-1108-0211, run-1108-0211-lb. Open vs vendor on one camera.
At a glance
The open stack passes 26 of 45 compared checks against the vendor stack; 7 of 19 failed checks still have no follow-up.
Key numbers, open vs vendor
Where a direction is defined, open is better in 13, equal (within 3 %) in 2 and worse in 7 rows. This direction chip is the raw open-vs-vendor comparison; pass/fail uses the tolerance of each row.
Contents
- At a glance
- Summary (full text)
- Findings and follow-up (19 rows)
- Deviations and warnings (6)
- Checks (pass/fail) (26 pass, 19 fail)
- Protocol (camera, stacks, lamps)
- A1 P1 defaults (default AE, comp 128)
- A2 Matched exposure + NV12 noise (P2)
- A3 Colour (dE00) and grey row
- A4 Sharpness (slanted edge at the chart edges)
- Pictures (33)
- Method
Summary (full text)
Camera wyze-t20x-jxf23 (T20) in the light box with the SpyderCheckr 24 chart, lamps 3000 (fair pass, supplement) + 5000 (colour pass) K. Both stacks ran on the same camera, the same chart position and the same light: the open stack (open-tx-isp + OpenIMP + streamer) and the vendor stack (Ingenic ISP module + vendor libimp + streamer built for it), one after the other.
Result: 26 pass, 19 fail (6 invalid by test setup, 4 vendor differences, 1 fixed, 1 accepted, 7 open), 2 info/warn, 11 planned checks not run in these runs.
Failed checks: chart grey BG (AWB, 1.0 = neutral) (open 0.9698, vendor 0.8991; pass = |open - vendor| <= 0.02); p2_a frame luma at the same exposure (open 93.7, vendor 71.9; pass = within +-3 %); p2_a NV12 temporal noise Y (Y 32..224) (open 1.417, vendor 1.232; pass = open <= vendor x (1 + 2 x A/A spread 5 %)); p2_a NV12 temporal noise V (open 0.439, vendor 0.383; pass = open <= vendor x (1 + 2 x A/A spread 5 %)); p2_b frame luma at the same exposure (open 92.4, vendor 55.4; pass = within +-3 %); p2_b NV12 temporal noise Y (Y 32..224) (open 2.037, vendor 1.702; pass = open <= vendor x (1 + 2 x A/A spread 5 %)); p2_c frame luma at the same exposure (open 91.6, vendor 82.3; pass = within +-3 %); p2_c NV12 temporal noise Y (Y 32..224) (open 1.406, vendor 1.152; pass = open <= vendor x (1 + 2 x A/A spread 5 %)).
Passed: frame luma p1_day (AE target, default comp 128); frame luma p1_night (AE target, default comp 128); chart grey RG (AWB, 1.0 = neutral); p2_a NV12 temporal noise U; p2_b NV12 temporal noise U; p2_b NV12 temporal noise V; p2_c NV12 temporal noise U; p2_night NV12 temporal noise Y (Y 32..224); p1_day dE00 mean, unclipped patches; p1_day dE00 mean, grey-normalised; ...
How to read: "pass" means the open stack is equal to or better than the vendor stack within the tolerance named in each row; matched-exposure rows (A2) compare the same integration time and gain on both stacks, read back from the ISP.
Findings and follow-up
19 rows
One row per failed or deviating (warn) check and how it is handled. Issue states were read from GitHub at generation time. The check name links to its row in the checks table.
| check | stacks | open | vendor | verdict | handling |
|---|---|---|---|---|---|
| chart grey BG (AWB, 1.0 = neutral) | open, vendor | 0.9698 | 0.8991 | fail | open no follow-up yet |
| p2_a frame luma at the same exposure | open, vendor | 93.7 | 71.9 | fail | vendor difference vendor gain step is non-linear, so matched exposure gives a different brightness |
| p2_a NV12 temporal noise Y (Y 32..224) | open, vendor | 1.417 | 1.232 | fail | open no follow-up yet |
| p2_a NV12 temporal noise V | open, vendor | 0.439 | 0.383 | fail | open no follow-up yet |
| p2_b frame luma at the same exposure | open, vendor | 92.4 | 55.4 | fail | vendor difference vendor gain step is non-linear, so matched exposure gives a different brightness |
| p2_b NV12 temporal noise Y (Y 32..224) | open, vendor | 2.037 | 1.702 | fail | open no follow-up yet |
| p2_c frame luma at the same exposure | open, vendor | 91.6 | 82.3 | fail | vendor difference vendor gain step is non-linear, so matched exposure gives a different brightness |
| p2_c NV12 temporal noise Y (Y 32..224) | open, vendor | 1.406 | 1.152 | fail | open no follow-up yet |
| p2_c NV12 temporal noise V | open, vendor | 0.544 | 0.457 | fail | open no follow-up yet |
| p2_night frame luma at the same exposure | open, vendor | 69.7 | 55.5 | fail | accepted night tone curve differs, low priority |
| grey row tone slope | open, vendor | 0.863 | 0.776 | fail | open no follow-up yet |
| OSD text item appears | open, vendor | 3.1 2.2 mean |diff| OSD corner / elsewhere | 3.1 2.2 mean |diff| OSD corner / elsewhere | fail | invalid (test setup) test method, the OSD change was too small to measure |
| hflip: picture mirrored | open, vendor | corr mirrored 0.977 / same 0.530 | corr mirrored 0.006 / same 0.046 | fail | vendor difference vendor hflip does not mirror the picture |
| max_again 0: applied AGain (x1024) <= cap | open, vendor | 31378 <= 1024 | 1024 <= 1024 | fail | fixed in open-tx-isp 36e7da60 (Lu-Fi/openimp#238, closed): applied AGain now respects the cap |
| cycles with module bound + valid picture | open | 0/10 | - | fail | invalid (test setup) T20 binds the sensor on stream open, not on module load; check fixed |
| SSIM/PSNR | - | - | - | fail | invalid (test setup) no RTSP credentials in the old run; fixed in the runner, the new run has credentials |
| RTSP client 1 frames in 60 s / errors | open, vendor | 0 / 4 | 0 / 4 | fail | invalid (test setup) no RTSP credentials in the old run; fixed in the runner, the new run has credentials |
| RTSP client 2 frames in 60 s / errors | open, vendor | 0 / 4 | 0 / 4 | fail | invalid (test setup) no RTSP credentials in the old run; fixed in the runner, the new run has credentials |
| RTSP client 3 frames in 60 s / errors | open, vendor | 0 / 4 | 0 / 4 | fail | invalid (test setup) no RTSP credentials in the old run; fixed in the runner, the new run has credentials |
Deviations and warnings
6
- vendor: 04:56:37 preload: libimp symbols missing (SetExpr/GetExpr/SetMaxAgain/SetMaxDgain)
- box thermometer value was stale (watcher not running): ambient temperature not known for this run
- Vendor stack: the SDK jxf23 sensor driver sets no line time (one_line_expr_in_us = 0), so the vendor ISP module cannot take a manual integration time; for this pass the vendor sensor module was built with 30 us (the value the SDK's other T20 sensors use). P1 (default AE) is not affected.
- Matched exposure (P2): the open stack is linear over 2x/4x/8x (frame Y 93.7/92.4/91.6); the vendor stack is not (71.9/55.4/82.3) at the same read-back exposure and gain, so open-vs-vendor brightness and noise at P2 are not comparable point by point (vendor gain-step quirk, as on the T10).
- Colour 5000 K (P1 day): dE00 to the chart open 11.85 vs vendor 13.27, white-balance-removed 6.95 vs 8.33, saturation C* ratio 0.83 vs 0.62: the open stack is closer to the chart and less washed out; open-vs-vendor dE00 mean 3.2.
- This protocol is from the night run 2026-11-08 (RC3 image); a complete run with the new runner (encoder, noise curve, substream, AE response, RTSP with credentials) on the next-state image follows and replaces it.
Checks (pass/fail)
26 pass, 19 fail
Cells: open / vendor value, Δ = open - vendor (relative to vendor), then the direction chip (better / equal / worse, where the row defines one) and the pass/fail verdict with its rule. Rows are grouped by test section.
| check | setting | open | vendor | Δ open-vendor | open vs vendor | verdict | rule / note | handling |
|---|---|---|---|---|---|---|---|---|
| A1 P1 defaults | ||||||||
| frame luma p1_day (AE target, default comp 128) | default AE, comp 128 | 105.8 | 113.5 | -7.7 (-7 %) | pass | pass = open within +-10 % of vendor | ||
| frame luma p1_night (AE target, default comp 128) | default AE, comp 128 | 80.7 | 78.4 | +2.3 (+3 %) | pass | pass = open within +-10 % of vendor | ||
| chart grey RG (AWB, 1.0 = neutral) | lamps 3000 (fair pass, supplement) + 5000 (colour pass) K | 1.0409 | 1.0468 | -0.0059 (-1 %) | equal | pass | pass = |open - vendor| <= 0.02 | |
| chart grey BG (AWB, 1.0 = neutral) | lamps 3000 (fair pass, supplement) + 5000 (colour pass) K | 0.9698 | 0.8991 | +0.0707 (+8 %) | better | fail | pass = |open - vendor| <= 0.02 | open no follow-up yet |
| A2 matched exposure | ||||||||
| p2_a frame luma at the same exposure | IT 1215 / AGain 32 / IspDG 0 | 93.7 | 71.9 | +21.8 (+30 %) | fail | pass = within +-3 % | vendor difference vendor gain step is non-linear, so matched exposure gives a different brightness | |
| p2_a NV12 temporal noise Y (Y 32..224) | IT 1215 / AGain 32 / IspDG 0 | 1.417 | 1.232 | +0.185 (+15 %) | worse | fail | pass = open <= vendor x (1 + 2 x A/A spread 5 %) | open no follow-up yet |
| p2_a NV12 temporal noise U | IT 1215 / AGain 32 / IspDG 0 | 0.607 | 0.742 | -0.135 (-18 %) | better | pass | pass = open <= vendor x (1 + 2 x A/A spread 5 %) | |
| p2_a NV12 temporal noise V | IT 1215 / AGain 32 / IspDG 0 | 0.439 | 0.383 | +0.056 (+15 %) | worse | fail | pass = open <= vendor x (1 + 2 x A/A spread 5 %) | open no follow-up yet |
| p2_b frame luma at the same exposure | IT 607 / AGain 64 / IspDG 0 | 92.4 | 55.4 | +37.0 (+67 %) | fail | pass = within +-3 % | vendor difference vendor gain step is non-linear, so matched exposure gives a different brightness | |
| p2_b NV12 temporal noise Y (Y 32..224) | IT 607 / AGain 64 / IspDG 0 | 2.037 | 1.702 | +0.335 (+20 %) | worse | fail | pass = open <= vendor x (1 + 2 x A/A spread 5 %) | open no follow-up yet |
| p2_b NV12 temporal noise U | IT 607 / AGain 64 / IspDG 0 | 0.846 | 1.052 | -0.206 (-20 %) | better | pass | pass = open <= vendor x (1 + 2 x A/A spread 5 %) | |
| p2_b NV12 temporal noise V | IT 607 / AGain 64 / IspDG 0 | 0.588 | 0.557 | +0.031 (+6 %) | worse | pass | pass = open <= vendor x (1 + 2 x A/A spread 5 %) | |
| p2_c frame luma at the same exposure | IT 304 / AGain 96 / IspDG 0 | 91.6 | 82.3 | +9.3 (+11 %) | fail | pass = within +-3 % | vendor difference vendor gain step is non-linear, so matched exposure gives a different brightness | |
| p2_c NV12 temporal noise Y (Y 32..224) | IT 304 / AGain 96 / IspDG 0 | 1.406 | 1.152 | +0.254 (+22 %) | worse | fail | pass = open <= vendor x (1 + 2 x A/A spread 5 %) | open no follow-up yet |
| p2_c NV12 temporal noise U | IT 304 / AGain 96 / IspDG 0 | 0.796 | 1.041 | -0.245 (-24 %) | better | pass | pass = open <= vendor x (1 + 2 x A/A spread 5 %) | |
| p2_c NV12 temporal noise V | IT 304 / AGain 96 / IspDG 0 | 0.544 | 0.457 | +0.087 (+19 %) | worse | fail | pass = open <= vendor x (1 + 2 x A/A spread 5 %) | open no follow-up yet |
| p2_night frame luma at the same exposure | IT 397 / AGain 32 / IspDG 0 | 69.7 | 55.5 | +14.2 (+26 %) | fail | pass = within +-3 % | accepted night tone curve differs, low priority | |
| p2_night NV12 temporal noise Y (Y 32..224) | IT 397 / AGain 32 / IspDG 0 | 1.772 | 1.811 | -0.039 (-2 %) | equal | pass | pass = open <= vendor x (1 + 2 x A/A spread 5 %) | |
| A/A repeatability (p2_a vs p2_a2, same point twice) | IT/gain of p2_a | Ytemp 0% U 0% V 1% | info | relative |a - a2|; > 15 % = the method is noisier than the difference sought | ||||
| A3 colour | ||||||||
| p1_day dE00 mean, unclipped patches | chart_de.py, lamps 3000 (fair pass, supplement) + 5000 (colour pass) K | 11.06 | 13.27 | -2.21 (-17 %) | better | pass | pass = open <= vendor + 1.0 | |
| p1_day dE00 mean, grey-normalised | chart_de.py, lamps 3000 (fair pass, supplement) + 5000 (colour pass) K | 6.95 | 8.33 | -1.38 (-17 %) | better | pass | pass = open <= vendor + 1.0 | |
| p2_a dE00 mean, unclipped patches | chart_de.py, lamps 3000 (fair pass, supplement) + 5000 (colour pass) K | 9.72 | 9.35 | +0.37 (+4 %) | worse | pass | pass = open <= vendor + 1.0 | |
| p2_a dE00 mean, grey-normalised | chart_de.py, lamps 3000 (fair pass, supplement) + 5000 (colour pass) K | 6.35 | 7.64 | -1.29 (-17 %) | better | pass | pass = open <= vendor + 1.0 | |
| grey row tone slope | P1 day | 0.863 | 0.776 | +0.087 (+11 %) | better | fail | pass = |open - vendor| <= 0.05 | open no follow-up yet |
| A4 sharpness | ||||||||
| MTF50 median of 8 chart edges (cycles/pixel) | P1 day, 8 frames averaged | 0.296 | 0.276 | +0.020 (+7 %) | better | pass | pass = open >= 0.9 x vendor | |
| 10-90 % edge rise median (px) | P1 day | 1.98 | 5.25 | -3.27 (-62 %) | better | pass | pass = open <= vendor x 1.1 | |
| MTF50 median / 10-90 rise median | defaults, 8 snapshots averaged | 0.274 c/p / 2.33 px (8 edges) | 0.300 c/p / 4.82 px (8 edges) | info | lb_analyse.py mtf; lb_analyse.py mtf | |||
| B3 CPU/RAM | ||||||||
| system busy % (idle streamer, 60 s) | 0 RTSP clients | 10.20 | 10.60 | -0.40 (-4 %) | better | pass | pass = open <= vendor + 0.5 points | |
| streamer % of all CPU | 0 RTSP clients | 9.08 | 9.88 | -0.80 (-8 %) | better | pass | pass = open <= vendor + 0.5 points | |
| ISP/codec kernel threads % | 0 RTSP clients | 0.03 | 2.50 | -2.47 (-99 %) | better | pass | pass = open <= vendor + 0.5 points | |
| C2 control API | ||||||||
| OSD text item appears | osd0.3 text item LBTEST at 40,40 | 3.1 2.2 mean |diff| OSD corner / elsewhere | 3.1 2.2 mean |diff| OSD corner / elsewhere | fail | pass = the top-left corner changes clearly more than the rest; pass = the top-left corner changes clearly more than the rest | invalid (test setup) test method, the OSD change was too small to measure | ||
| comp 40 / 200 frame Y | ae_compensation 40 vs 200 | 75.8 / 198.7 Y | 54.5 / 220.2 Y | pass | pass = 200 brighter by > 10; pass = 200 brighter by > 10 | |||
| hflip: picture mirrored | image.hflip 1 | corr mirrored 0.977 / same 0.530 | corr mirrored 0.006 / same 0.046 | fail | pass = the new picture matches the mirrored old one; pass = the new picture matches the mirrored old one | vendor difference vendor hflip does not mirror the picture | ||
| max_again 0: applied AGain (x1024) <= cap | max_again 0 (1x) at comp 255 | 31378 <= 1024 | 1024 <= 1024 | fail | ISP mode=Day it=2022 ag=158 dg=0 idg=0 tg=7844 mag=0; ISP mode=Day it=2022 ag=0 dg=0 idg=80 tg=1448 mag=0 | fixed in open-tx-isp 36e7da60 (Lu-Fi/openimp#238, closed): applied AGain now respects the cap | ||
| C1 D1/D4 | ||||||||
| D1 dark start: ISP night | streamer daynight trace, dark start, lamps on 180 s, off 180 s | mode=Night | - | pass | 60 s after start in the dark | |||
| D4 lamps off: ISP night after 180 s | streamer daynight trace, dark start, lamps on 180 s, off 180 s | mode=Night | - | pass | ||||
| D4 lamps on: ISP day after 180 s | streamer daynight trace, dark start, lamps on 180 s, off 180 s | mode=Day | - | pass | ||||
| B4 reload | ||||||||
| cycles with module bound + valid picture | 10 cycles | 0/10 | - | fail | picture: 1 snapshot, Y std > 2 | invalid (test setup) T20 binds the sensor on stream open, not on module load; check fixed | ||
| cycles with oops/BUG lines in dmesg | 10 cycles | 0 | - | pass | ||||
| i2c sensor client released after rmmod | 10 cycles | 10/10 | - | pass | sysfs 0-00xx driver link gone after rmmod | |||
| B1 SSIM/PSNR | ||||||||
| SSIM/PSNR | defaults, 8 snapshots averaged | - | - | fail | a capture failed (see $S/audit/issue230/t20/run-1108-0211-lb/open/ssim/*.err); a capture failed (see $S/audit/issue230/t20/run-1108-0211-lb/vendor/ssim/*.err) | invalid (test setup) no RTSP credentials in the old run; fixed in the runner, the new run has credentials | ||
| C3 stability | ||||||||
| RTSP client 1 frames in 60 s / errors | 3 RTSP clients (ch0, ch0, ch1) for 60 s + 10 snapshots during the load | 0 / 4 | 0 / 4 | fail | pass = > 100 frames, no ffmpeg errors; pass = > 100 frames, no ffmpeg errors | invalid (test setup) no RTSP credentials in the old run; fixed in the runner, the new run has credentials | ||
| RTSP client 2 frames in 60 s / errors | 3 RTSP clients (ch0, ch0, ch1) for 60 s + 10 snapshots during the load | 0 / 4 | 0 / 4 | fail | pass = > 100 frames, no ffmpeg errors; pass = > 100 frames, no ffmpeg errors | invalid (test setup) no RTSP credentials in the old run; fixed in the runner, the new run has credentials | ||
| RTSP client 3 frames in 60 s / errors | 3 RTSP clients (ch0, ch0, ch1) for 60 s + 10 snapshots during the load | 0 / 4 | 0 / 4 | fail | pass = > 100 frames, no ffmpeg errors; pass = > 100 frames, no ffmpeg errors | invalid (test setup) no RTSP credentials in the old run; fixed in the runner, the new run has credentials | ||
| config persistence across restart | POST image.ae_compensation 140, restart with the same /tmp conf, GET | ae_compensation 140 | ae_compensation 140 | pass | /tmp conf copy only (flash untouched); /tmp conf copy only (flash untouched) | |||
| snapshot after streamer restart | streamer restart (kill + start, same conf) | 105.3 51.1 1 Y std n | 114.5 50.7 1 Y std n | pass | ||||
| snapshots under load valid | 3 RTSP clients (ch0, ch0, ch1) for 60 s + 10 snapshots during the load | 10/10 | 10/10 | pass | times in stab/snap_times.txt; times in stab/snap_times.txt | |||
| Planned, not run in these runs | ||||||||
| A1b: AE comp sweep 40..200 + path test | - | - | - | not run | not part of the runs in this report | |||
| A2b: AWB 3000/5000 K + WB presets | - | - | - | not run | not part of the runs in this report | |||
| A1c: AE step response (lamps on/off) | - | - | - | not run | not part of the runs in this report | |||
| A2c: noise vs analog gain | - | - | - | not run | not part of the runs in this report | |||
| A4b: substream MTF / colour / NV12 layout | - | - | - | not run | not part of the runs in this report | |||
| A5: encoder H.264/H.265: FixQP bytes, CBR quality | - | - | - | not run | not part of the runs in this report | |||
| A6: banding / flicker | - | - | - | not run | not part of the runs in this report | |||
| A7: night IR picture | - | - | - | not run | not part of the runs in this report | |||
| B2: RC modes CBR/VBR/FixQP + bitrate jump | - | - | - | not run | not part of the runs in this report | |||
| B5: startup time | - | - | - | not run | not part of the runs in this report | |||
| C4: motion detection (PTZ cams) | - | - | - | not run | not part of the runs in this report | |||
Protocol (camera, stacks, lamps)
| item | value |
|---|---|
| camera | wyze-t20x-jxf23 (T20, sensor jxf23), position: light box, chart centred, camera at the card centre (fixed, no PTZ) |
| run dirs | run-1108-0211, run-1108-0211-lb |
| open stack time (run-1108-0211) | 2026-11-08 02:11:16 .. 02:28:18 |
| open stack time (run-1108-0211-lb) | 2026-11-08 03:01:44 .. 04:55:29 |
| vendor stack time (run-1108-0211) | 2026-11-08 02:28:40 .. 02:58:21 |
| vendor stack time (run-1108-0211-lb) | 2026-11-08 04:55:47 .. 06:02:34 |
| lamps | CT 3000 (fair pass, supplement) + 5000 (colour pass) K (2 x DiCUNO E14 5 W CRI 98 per CT, DC LED) |
| lamp marker | 02:24:24 NEED_LAMPS_OFF_1 (run-1108-0211) |
| lamp marker | 02:24:45 LAMPS_OFF_1 (run-1108-0211) |
| lamp marker | 02:28:40 NEED_LAMPS_ON (run-1108-0211) |
| lamp marker | 02:28:52 LAMPS_ON (run-1108-0211) |
| lamp marker | 02:53:19 NEED_LAMPS_OFF_2 (run-1108-0211) |
| lamp marker | 02:53:40 LAMPS_OFF_2 (run-1108-0211) |
| lamp marker | 02:58:21 NEED_LAMPS_ON_S (run-1108-0211) |
| lamp marker | 02:58:40 LAMPS_ON_S (run-1108-0211) |
| lamp marker | 04:41:46 NEED_LAMPS_OFF_1 (run-1108-0211-lb) |
| lamp marker | 04:42:03 LAMPS_OFF_1 (run-1108-0211-lb) |
| lamp marker | 04:45:04 NEED_LAMPS_ON_2 (run-1108-0211-lb) |
| lamp marker | 04:45:18 LAMPS_ON_2 (run-1108-0211-lb) |
| lamp marker | 04:48:20 NEED_LAMPS_OFF_3 (run-1108-0211-lb) |
| lamp marker | 04:48:34 LAMPS_OFF_3 (run-1108-0211-lb) |
| lamp marker | 04:52:02 NEED_LAMPS_ON_4 (run-1108-0211-lb) |
| lamp marker | 04:52:26 LAMPS_ON_4 (run-1108-0211-lb) |
| box temperature | 21.8 C (STALE: thermometer not logged during the run) |
| flashed image / build | wyze_cam2_t20x_jxf23_rtl8189ftv build="claude/aperto-port-1107+725a14d, 2026-11-07 19:53:50 UTC" |
| open: ko file in /lib/modules (flashed; the loaded ko is under stack pieces) / libimp mapped | tx_isp_t20 md5=9c3d9681 libimp=0c0132c8 |
| open: streamer | streamer bin_md5=b8faed54 version=v1.12.0 |
| open: streamer version (/control) | v1.12.0 |
| vendor: ko file in /lib/modules (flashed; the loaded ko is under stack pieces) / libimp mapped | tx_isp_t20 md5=9c3d9681 libimp=da38e940 |
| vendor: streamer | streamer bin_md5=904a2785 version=v1.12.0-vendorlib |
| vendor: streamer version (/control) | v1.12.0-vendorlib |
| open stack pieces (md5) | tx-isp-t20.ko 9c3d9681 (run-1108-0211), sensor_jxf23_t20.ko 5f0c6499 (run-1108-0211), libimp.so 0c0132c8 (run-1108-0211), streamer b8faed54 (run-1108-0211), jxf23.bin db5536f9 (run-1108-0211) |
| vendor stack pieces (md5) | libalog.so 90a3c90d (run-1108-0211), libimp.so da38e940 (run-1108-0211), libsysutils.so 3e9d3e8b (run-1108-0211), tx-isp-t20.ko 13169fc2 (run-1108-0211), sensor_jxf23_t20.ko 9bc0c394 (run-1108-0211), streamer 904a2785 (run-1108-0211), fair_preload.so 8d8cfe9c (run-1108-0211), jxf23.bin db5536f9 (run-1108-0211), tx-isp-t20.ko 9c3d9681 (run-1108-0211) |
| open stack | open-tx-isp tx_isp_t20 (flashed, aperto-port 725a14d RC3) + OpenIMP libimp (flashed) + installed streamer v1.12.0 under LD_PRELOAD fair_preload_t20.so |
| vendor stack | ingenic-sdk tx-isp-t20.ko (vendor ISP blob libt20-firmware-3.12.0) + sensor_jxf23_t20.ko (vendor-compatible rebuild) + vendor libimp 3.12.0 (da38e940) + streamer v1.12.0 USE_OPENIMP=0 |
A1 P1 defaults (default AE, comp 128)
| stack | phase | mode | IT (lines) | AGain | IspDG | EV | jpg Y (0..255) | R/G | B/G | clipped | time | run |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| open | p1_day | Day | 1011 | 57 | 0 | 879 | 105.8 | 1.0409 | 0.9698 | 0.0 | 02:17:45 | run-1108-0211 |
| vendor | p1_day | Day | 1011 | 64 | 2 | 1069 | 113.5 | 1.0468 | 0.8991 | 0.0 | 02:46:31 | run-1108-0211 |
| open | p1_night | Night | 337 | 56 | 0 | 861 | 80.7 | - | - | - | 02:27:22 | run-1108-0211 |
| vendor | p1_night | Night | 337 | 52 | 3 | 842 | 78.4 | - | - | - | 02:56:18 | run-1108-0211 |
A2 Matched exposure + NV12 noise (P2)
Same integration time (lines) and gains (x1024) forced on both stacks through the streamer, read back from the ISP (verify). NV12: 8 frames of channel 0 (1920x1080), centre half, temporal std per pixel.
| stack | phase | verify | mode | IT (lines) | AGain | IspDG | EV | jpg Y (0..255) | NV12 Y (0..255) | Ytemp (std) | U (std) | V (std) | drift | time | run |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| open | p2_a | ok | Day | 1215 | 32 | 0 | 512 | 93.7 | 129.95 | 1.417 | 0.607 | 0.439 | 0.02 | 02:19:14 | run-1108-0211 |
| vendor | p2_a | ok | Day | 1215 | 32 | 0 | 512 | 71.9 | 108.77 | 1.232 | 0.742 | 0.383 | 0.01 | 02:48:01 | run-1108-0211 |
| open | p2_a2 | ok | Day | 1215 | 32 | 0 | 512 | 93.6 | - | - | - | - | - | 02:22:45 | run-1108-0211 |
| vendor | p2_a2 | ok | Day | 1215 | 32 | 0 | 512 | 71.8 | 108.73 | 1.232 | 0.742 | 0.380 | 0.01 | 02:51:38 | run-1108-0211 |
| open | p2_b | ok | Day | 607 | 64 | 0 | 1024 | 92.4 | 128.55 | 2.037 | 0.846 | 0.588 | 0.03 | 02:20:27 | run-1108-0211 |
| vendor | p2_b | ok | Day | 607 | 64 | 0 | 1024 | 55.4 | 84.86 | 1.702 | 1.052 | 0.557 | 0.07 | 02:49:12 | run-1108-0211 |
| open | p2_c | ok | Day | 304 | 96 | 0 | 2048 | 91.6 | 128.08 | 1.406 | 0.796 | 0.544 | 0.05 | 02:21:40 | run-1108-0211 |
| vendor | p2_c | ok | Day | 304 | 96 | 0 | 2048 | 82.3 | 117.88 | 1.152 | 1.041 | 0.457 | 0.03 | 02:50:25 | run-1108-0211 |
| open | p2_night | ok | Night | 397 | 32 | 0 | 512 | 69.7 | 126.18 | 1.772 | - | - | 0.71 | 02:28:17 | run-1108-0211 |
| vendor | p2_night | ok | Night | 397 | 32 | 0 | 512 | 55.5 | 108.45 | 1.811 | - | - | 1.00 | 02:57:11 | run-1108-0211 |
A3 Colour (dE00) and grey row
chart_de.py (audit/issue230/colour) per phase, cells = open / vendor. Lower dE00 is better.
| phase | dE00 mean | dE00 median | dE00 max | dE00 mean, unclipped patches | dE00 mean, grey-normalised | chroma ratio (1 = reference) | dE00 skin patches | grey a* cast | grey b* cast | gamma fit |
|---|---|---|---|---|---|---|---|---|---|---|
| p1_day | 11.85 / 13.27 | 11.50 / 11.73 | 25.26 / 31.66 | 11.06 / 13.27 | 6.95 / 8.33 | 0.83 / 0.62 | 13.71 / 14.57 | -0.15 / -0.52 | 3.99 / 2.76 | 0.86 / 0.76 |
| p2_a | 9.72 / 9.35 | 9.34 / 7.92 | 18.30 / 16.49 | 9.72 / 9.35 | 6.35 / 7.64 | 0.88 / 0.94 | 12.51 / 14.49 | -0.36 / -0.60 | 5.60 / 7.48 | 0.94 / 1.12 |
Suite colour-cct, P1 day
audit/chart/spyder.py: sRGB -> Lab D50, CIEDE2000, reference = Datacolor default values, D50 assumed, not batch specific.
| metric | open | vendor | Δ open-vendor | open vs vendor |
|---|---|---|---|---|
| dE00 mean (raw) | 11.45 | 11.84 | -0.39 (-3 %) | better |
| dE00 mean (grey-normalised) | 9.22 | 10.31 | -1.09 (-11 %) | better |
| dE00 colour patches | 12.35 | 12.37 | -0.02 (-0 %) | equal |
| dE00 grey patches | 8.76 | 10.26 | -1.50 (-15 %) | better |
| dE00 max | 29.44 | 33.58 | -4.14 (-12 %) | better |
| chroma ratio (1 = reference) | 0.62 | 0.70 | -0.08 (-11 %) | worse |
| mean hue error deg | 8.96 | 15.54 | -6.58 (-42 %) | better |
| grey a* cast | 0.42 | 0.96 | -0.54 (-56 %) | better |
| grey b* cast | 4.34 | 5.02 | -0.68 (-14 %) | better |
dE00 per patch, P1 day
| patch | open | vendor | patch | open | vendor |
|---|---|---|---|---|---|
| A1 | 25.3 | 29.1 | C1 | 19.2 | 21.1 |
| A2 | 15.6 | 19.4 | C2 | 8.9 | 8.3 |
| A3 | 5.9 | 8.5 | C3 | 11.7 | 12.9 |
| A4 | 3.5 | 3.4 | C4 | 6.6 | 6.6 |
| A5 | 3.8 | 3.9 | C5 | 12.9 | 15.5 |
| A6 | 4.8 | 4.8 | C6 | 20.8 | 24.1 |
| B1 | 7.0 | 7.7 | D1 | 16.4 | 17.3 |
| B2 | 10.4 | 12.7 | D2 | 14.6 | 14.4 |
| B3 | 24.7 | 31.7 | D3 | 11.3 | 10.7 |
| B4 | 8.3 | 11.5 | D4 | 11.7 | 11.7 |
| B5 | 6.8 | 7.8 | D5 | 15.0 | 17.3 |
| B6 | 6.8 | 6.4 | D6 | 12.4 | 11.8 |
Grey row A1..A6
| metric | open | vendor | Δ open-vendor | open vs vendor |
|---|---|---|---|---|
| tone slope log-log (1 = reference gamma) | 0.863 | 0.776 | +0.087 (+11 %) | better |
| mean dL | -8.508 | -8.428 | -0.080 (+1 %) | |
| max |dL| | 31.524 | 33.646 | -2.122 (-6 %) | better |
| white A1 code | 246.358 | 241.426 | +4.932 (+2 %) | |
| black A6 code | 57.702 | 64.956 | -7.254 (-11 %) | |
| clipped greys | 0.000 | 0.000 | +0.000 | equal |
| range ratio | 22.064 | 16.713 | +5.351 (+32 %) | |
| code A1 | 246.358 | 241.426 | +4.932 (+2 %) | |
| code A2 | 245.000 | 239.856 | +5.144 (+2 %) | |
| code A3 | 242.555 | 240.000 | +2.555 (+1 %) | |
| code A4 | 198.275 | 209.076 | -10.801 (-5 %) | |
| code A5 | 128.076 | 138.585 | -10.509 (-8 %) | |
| code A6 | 57.702 | 64.956 | -7.254 (-11 %) |
A4 Sharpness (slanted edge at the chart edges)
Outer edge of the two middle patches on every chart side (patch vs black chart frame), 8 frames averaged, edge ESF with sub-pixel registration from the small chart tilt (lbmetrics.edge_sharpness): 10-90 % rise in pixels (lower = sharper) and MTF50 in cycles/pixel (higher = sharper).
| edge | rise open (px) | rise vendor (px) | MTF50 open (c/p) | MTF50 vendor (c/p) | contrast o | contrast v | tilt (deg) |
|---|---|---|---|---|---|---|---|
| top-20 | 1.78 | 2.16 | 0.267 | 0.215 | 77 | 76 | 0.7 |
| top-30 | 2.06 | 10.08 | 0.254 | 0.279 | 113 | 101 | 4.2 |
| bottom-23 | 1.22 | 1.54 | 0.367 | 0.328 | 80 | 88 | 4.6 |
| bottom-33 | 1.90 | 2.25 | 0.266 | 0.244 | 57 | 57 | 2.5 |
| left-01 | 11.28 | 11.50 | 0.468 | 0.415 | 45 | 45 | -11.8 |
| left-02 | 4.04 | 2.36 | 0.536 | 0.439 | 150 | 143 | -7.3 |
| right-51 | 2.26 | 8.15 | 0.300 | 0.273 | 70 | 78 | 3.6 |
| right-52 | 1.58 | 10.49 | 0.292 | 0.206 | 114 | 108 | 0.3 |
Pictures
33
Downscaled to 800 px, JPEG q75. Chart and grey card only. Click a picture to see it large (Esc or click closes).
A1 p1_day
A1 p1_night
A2 p2_a
A2 p2_night
A3 dE00 overlay p1_day (chart_de.py)
A3 dE00 overlay p2_a (chart_de.py)
A4 MTF crops
C2 control API OSD text item appears
C2 control API OSD text item appears
C2 control API comp 40 / 200 frame Y
C2 control API comp 40 / 200 frame Y
C2 control API comp 40 / 200 frame Y
C2 control API comp 40 / 200 frame Y
C2 control API hflip: picture mirrored
C2 control API hflip: picture mirrored
C2 control API max_again 0: applied AGain (x1024) <= cap
C2 control API max_again 0: applied AGain (x1024) <= cap
B4 reload cycles with oops/BUG lines in dmesg
C3 stability snapshots under load valid
C3 stability snapshots under load valid
Method
- Same camera, same chart position, same lamps; open stack first, then the vendor ISP module + vendor libimp are loaded from /tmp (nothing flashed); the camera returns to the flashed image at the end (module reload or reboot).
- P1 = camera defaults (AE, AWB, comp 128). P2 = matched exposure: the same integration time and gains on both stacks, set inside the streamer and read back from the ISP; rows whose read-back differs are marked verify=FAIL and not compared.
- A/A: point p2_a is measured twice (p2_a, p2_a2); the relative difference is the repeatability of the method. A difference between the stacks counts only beyond 2 x that spread (floor 5 %).
- NV12 noise is measured on raw NV12 frames from the streamer (no JPEG in the path): temporal standard deviation per pixel over the burst, mean over Y 32..224, and U/V.
- Colour: SpyderCheckr 24 detected automatically, sRGB -> Lab (D50), CIEDE2000 against the Datacolor default values (D50 assumed, not batch specific); 'grey-normalised' removes the white-balance cast first.
- Sharpness: slanted-edge method on the chart's outer patch edges, frames averaged against noise; JPEG snapshot path.
- SSIM/PSNR (when run): both stacks record the same static chart scene at the same bitrate; each stream is decoded and compared against a high-bitrate capture of the SAME stack (reference), so the number is the encoder loss of that stack; a cross-stack comparison on static frames is listed separately and includes the ISP difference.
- Encoder (A5): both stacks get the same streamer settings (codec, rc_mode, qp / bitrate, gop) through a /tmp conf and a streamer restart; the recordings come over RTSP (ffmpeg -c copy). The reference is a mean image, because single NV12 frames are not frame-aligned with the stream; the scene is static, so every decoded frame can be compared with it. A second reference at the end of the phase shows whether the scene or the AE moved in between.
- Lamp switches are made by a person on a marker (NEED_* file); the times above are the marker times.