From 726e9274ea95fa46352556d340c5793a8da51fcd Mon Sep 17 00:00:00 2001 From: Naushir Patuck Date: Wed, 3 May 2023 13:20:27 +0100 Subject: pipeline: ipa: raspberrypi: Refactor and move the Raspberry Pi code Split the Raspberry Pi pipeline handler and IPA source code into common and VC4/BCM2835 specific file structures. For the pipeline handler, the common code files now live in src/libcamera/pipeline/rpi/common/ and the VC4-specific files in src/libcamera/pipeline/rpi/vc4/. For the IPA, the common code files now live in src/ipa/rpi/{cam_helper,controller}/ and the vc4 specific files in src/ipa/rpi/vc4/. With this change, the camera tuning files are now installed under share/libcamera/ipa/rpi/vc4/. To build the pipeline and IPA, the meson configuration options have now changed from "raspberrypi" to "rpi/vc4": meson setup build -Dipas=rpi/vc4 -Dpipelines=rpi/vc4 Signed-off-by: Naushir Patuck Reviewed-by: Jacopo Mondi Reviewed-by: Laurent Pinchart Signed-off-by: Laurent Pinchart --- .reuse/dep5 | 2 +- Documentation/environment_variables.rst | 4 +- Documentation/guides/introduction.rst | 2 +- Documentation/guides/pipeline-handler.rst | 2 +- include/libcamera/ipa/meson.build | 2 +- meson.build | 2 +- meson_options.txt | 4 +- src/ipa/raspberrypi/README.md | 25 - src/ipa/raspberrypi/cam_helper.cpp | 265 --- src/ipa/raspberrypi/cam_helper.h | 132 -- src/ipa/raspberrypi/cam_helper_imx219.cpp | 115 - src/ipa/raspberrypi/cam_helper_imx290.cpp | 68 - src/ipa/raspberrypi/cam_helper_imx296.cpp | 83 - src/ipa/raspberrypi/cam_helper_imx477.cpp | 197 -- src/ipa/raspberrypi/cam_helper_imx519.cpp | 196 -- src/ipa/raspberrypi/cam_helper_imx708.cpp | 359 --- src/ipa/raspberrypi/cam_helper_ov5647.cpp | 109 - src/ipa/raspberrypi/cam_helper_ov9281.cpp | 66 - src/ipa/raspberrypi/controller/af_algorithm.h | 76 - src/ipa/raspberrypi/controller/af_status.h | 35 - src/ipa/raspberrypi/controller/agc_algorithm.h | 33 - src/ipa/raspberrypi/controller/agc_status.h | 37 - src/ipa/raspberrypi/controller/algorithm.cpp | 56 - src/ipa/raspberrypi/controller/algorithm.h | 68 - src/ipa/raspberrypi/controller/alsc_status.h | 22 - src/ipa/raspberrypi/controller/awb_algorithm.h | 25 - src/ipa/raspberrypi/controller/awb_status.h | 20 - .../raspberrypi/controller/black_level_status.h | 15 - src/ipa/raspberrypi/controller/camera_mode.h | 59 - src/ipa/raspberrypi/controller/ccm_algorithm.h | 21 - src/ipa/raspberrypi/controller/ccm_status.h | 14 - .../raspberrypi/controller/contrast_algorithm.h | 22 - src/ipa/raspberrypi/controller/contrast_status.h | 20 - src/ipa/raspberrypi/controller/controller.cpp | 181 -- src/ipa/raspberrypi/controller/controller.h | 73 - src/ipa/raspberrypi/controller/denoise_algorithm.h | 23 - src/ipa/raspberrypi/controller/denoise_status.h | 16 - src/ipa/raspberrypi/controller/device_status.cpp | 31 - src/ipa/raspberrypi/controller/device_status.h | 43 - src/ipa/raspberrypi/controller/dpc_status.h | 13 - src/ipa/raspberrypi/controller/geq_status.h | 14 - src/ipa/raspberrypi/controller/histogram.cpp | 64 - src/ipa/raspberrypi/controller/histogram.h | 53 - src/ipa/raspberrypi/controller/lux_status.h | 23 - src/ipa/raspberrypi/controller/metadata.h | 126 - src/ipa/raspberrypi/controller/noise_status.h | 14 - src/ipa/raspberrypi/controller/pdaf_data.h | 24 - src/ipa/raspberrypi/controller/pwl.cpp | 269 --- src/ipa/raspberrypi/controller/pwl.h | 127 - src/ipa/raspberrypi/controller/region_stats.h | 123 - src/ipa/raspberrypi/controller/rpi/af.cpp | 797 ------- src/ipa/raspberrypi/controller/rpi/af.h | 165 -- src/ipa/raspberrypi/controller/rpi/agc.cpp | 922 -------- src/ipa/raspberrypi/controller/rpi/agc.h | 133 -- src/ipa/raspberrypi/controller/rpi/alsc.cpp | 865 ------- src/ipa/raspberrypi/controller/rpi/alsc.h | 174 -- src/ipa/raspberrypi/controller/rpi/awb.cpp | 734 ------ src/ipa/raspberrypi/controller/rpi/awb.h | 191 -- src/ipa/raspberrypi/controller/rpi/black_level.cpp | 66 - src/ipa/raspberrypi/controller/rpi/black_level.h | 30 - src/ipa/raspberrypi/controller/rpi/ccm.cpp | 199 -- src/ipa/raspberrypi/controller/rpi/ccm.h | 75 - src/ipa/raspberrypi/controller/rpi/contrast.cpp | 181 -- src/ipa/raspberrypi/controller/rpi/contrast.h | 51 - src/ipa/raspberrypi/controller/rpi/dpc.cpp | 59 - src/ipa/raspberrypi/controller/rpi/dpc.h | 32 - src/ipa/raspberrypi/controller/rpi/focus.h | 28 - src/ipa/raspberrypi/controller/rpi/geq.cpp | 89 - src/ipa/raspberrypi/controller/rpi/geq.h | 34 - src/ipa/raspberrypi/controller/rpi/lux.cpp | 115 - src/ipa/raspberrypi/controller/rpi/lux.h | 45 - src/ipa/raspberrypi/controller/rpi/noise.cpp | 89 - src/ipa/raspberrypi/controller/rpi/noise.h | 32 - src/ipa/raspberrypi/controller/rpi/sdn.cpp | 80 - src/ipa/raspberrypi/controller/rpi/sdn.h | 32 - src/ipa/raspberrypi/controller/rpi/sharpen.cpp | 92 - src/ipa/raspberrypi/controller/rpi/sharpen.h | 34 - src/ipa/raspberrypi/controller/sharpen_algorithm.h | 21 - src/ipa/raspberrypi/controller/sharpen_status.h | 20 - src/ipa/raspberrypi/data/imx219.json | 486 ---- src/ipa/raspberrypi/data/imx219_noir.json | 402 ---- src/ipa/raspberrypi/data/imx290.json | 200 -- src/ipa/raspberrypi/data/imx296.json | 537 ----- src/ipa/raspberrypi/data/imx296_mono.json | 233 -- src/ipa/raspberrypi/data/imx378.json | 413 ---- src/ipa/raspberrypi/data/imx477.json | 518 ----- src/ipa/raspberrypi/data/imx477_noir.json | 429 ---- src/ipa/raspberrypi/data/imx477_scientific.json | 479 ---- src/ipa/raspberrypi/data/imx477_v1.json | 516 ----- src/ipa/raspberrypi/data/imx519.json | 413 ---- src/ipa/raspberrypi/data/imx708.json | 556 ----- src/ipa/raspberrypi/data/imx708_noir.json | 556 ----- src/ipa/raspberrypi/data/imx708_wide.json | 459 ---- src/ipa/raspberrypi/data/imx708_wide_noir.json | 459 ---- src/ipa/raspberrypi/data/meson.build | 26 - src/ipa/raspberrypi/data/ov5647.json | 487 ---- src/ipa/raspberrypi/data/ov5647_noir.json | 403 ---- src/ipa/raspberrypi/data/ov9281_mono.json | 123 - src/ipa/raspberrypi/data/se327m12.json | 418 ---- src/ipa/raspberrypi/data/uncalibrated.json | 118 - src/ipa/raspberrypi/md_parser.h | 155 -- src/ipa/raspberrypi/md_parser_smia.cpp | 149 -- src/ipa/raspberrypi/meson.build | 68 - src/ipa/raspberrypi/raspberrypi.cpp | 1853 --------------- src/ipa/raspberrypi/statistics.h | 78 - src/ipa/rpi/README.md | 25 + src/ipa/rpi/cam_helper/cam_helper.cpp | 265 +++ src/ipa/rpi/cam_helper/cam_helper.h | 132 ++ src/ipa/rpi/cam_helper/cam_helper_imx219.cpp | 115 + src/ipa/rpi/cam_helper/cam_helper_imx290.cpp | 68 + src/ipa/rpi/cam_helper/cam_helper_imx296.cpp | 83 + src/ipa/rpi/cam_helper/cam_helper_imx477.cpp | 197 ++ src/ipa/rpi/cam_helper/cam_helper_imx519.cpp | 196 ++ src/ipa/rpi/cam_helper/cam_helper_imx708.cpp | 359 +++ src/ipa/rpi/cam_helper/cam_helper_ov5647.cpp | 109 + src/ipa/rpi/cam_helper/cam_helper_ov9281.cpp | 66 + src/ipa/rpi/cam_helper/md_parser.h | 155 ++ src/ipa/rpi/cam_helper/md_parser_smia.cpp | 149 ++ src/ipa/rpi/cam_helper/meson.build | 26 + src/ipa/rpi/controller/af_algorithm.h | 76 + src/ipa/rpi/controller/af_status.h | 35 + src/ipa/rpi/controller/agc_algorithm.h | 33 + src/ipa/rpi/controller/agc_status.h | 37 + src/ipa/rpi/controller/algorithm.cpp | 56 + src/ipa/rpi/controller/algorithm.h | 68 + src/ipa/rpi/controller/alsc_status.h | 22 + src/ipa/rpi/controller/awb_algorithm.h | 25 + src/ipa/rpi/controller/awb_status.h | 20 + src/ipa/rpi/controller/black_level_status.h | 15 + src/ipa/rpi/controller/camera_mode.h | 59 + src/ipa/rpi/controller/ccm_algorithm.h | 21 + src/ipa/rpi/controller/ccm_status.h | 14 + src/ipa/rpi/controller/contrast_algorithm.h | 22 + src/ipa/rpi/controller/contrast_status.h | 20 + src/ipa/rpi/controller/controller.cpp | 181 ++ src/ipa/rpi/controller/controller.h | 73 + src/ipa/rpi/controller/denoise_algorithm.h | 23 + src/ipa/rpi/controller/denoise_status.h | 16 + src/ipa/rpi/controller/device_status.cpp | 31 + src/ipa/rpi/controller/device_status.h | 43 + src/ipa/rpi/controller/dpc_status.h | 13 + src/ipa/rpi/controller/geq_status.h | 14 + src/ipa/rpi/controller/histogram.cpp | 64 + src/ipa/rpi/controller/histogram.h | 53 + src/ipa/rpi/controller/lux_status.h | 23 + src/ipa/rpi/controller/meson.build | 29 + src/ipa/rpi/controller/metadata.h | 126 + src/ipa/rpi/controller/noise_status.h | 14 + src/ipa/rpi/controller/pdaf_data.h | 24 + src/ipa/rpi/controller/pwl.cpp | 269 +++ src/ipa/rpi/controller/pwl.h | 127 + src/ipa/rpi/controller/region_stats.h | 123 + src/ipa/rpi/controller/rpi/af.cpp | 797 +++++++ src/ipa/rpi/controller/rpi/af.h | 165 ++ src/ipa/rpi/controller/rpi/agc.cpp | 922 ++++++++ src/ipa/rpi/controller/rpi/agc.h | 133 ++ src/ipa/rpi/controller/rpi/alsc.cpp | 865 +++++++ src/ipa/rpi/controller/rpi/alsc.h | 174 ++ src/ipa/rpi/controller/rpi/awb.cpp | 734 ++++++ src/ipa/rpi/controller/rpi/awb.h | 191 ++ src/ipa/rpi/controller/rpi/black_level.cpp | 66 + src/ipa/rpi/controller/rpi/black_level.h | 30 + src/ipa/rpi/controller/rpi/ccm.cpp | 199 ++ src/ipa/rpi/controller/rpi/ccm.h | 75 + src/ipa/rpi/controller/rpi/contrast.cpp | 181 ++ src/ipa/rpi/controller/rpi/contrast.h | 51 + src/ipa/rpi/controller/rpi/dpc.cpp | 59 + src/ipa/rpi/controller/rpi/dpc.h | 32 + src/ipa/rpi/controller/rpi/focus.h | 28 + src/ipa/rpi/controller/rpi/geq.cpp | 89 + src/ipa/rpi/controller/rpi/geq.h | 34 + src/ipa/rpi/controller/rpi/lux.cpp | 115 + src/ipa/rpi/controller/rpi/lux.h | 45 + src/ipa/rpi/controller/rpi/noise.cpp | 89 + src/ipa/rpi/controller/rpi/noise.h | 32 + src/ipa/rpi/controller/rpi/sdn.cpp | 80 + src/ipa/rpi/controller/rpi/sdn.h | 32 + src/ipa/rpi/controller/rpi/sharpen.cpp | 92 + src/ipa/rpi/controller/rpi/sharpen.h | 34 + src/ipa/rpi/controller/sharpen_algorithm.h | 21 + src/ipa/rpi/controller/sharpen_status.h | 20 + src/ipa/rpi/controller/statistics.h | 78 + src/ipa/rpi/meson.build | 13 + src/ipa/rpi/vc4/data/imx219.json | 486 ++++ src/ipa/rpi/vc4/data/imx219_noir.json | 402 ++++ src/ipa/rpi/vc4/data/imx290.json | 200 ++ src/ipa/rpi/vc4/data/imx296.json | 537 +++++ src/ipa/rpi/vc4/data/imx296_mono.json | 233 ++ src/ipa/rpi/vc4/data/imx378.json | 413 ++++ src/ipa/rpi/vc4/data/imx477.json | 518 +++++ src/ipa/rpi/vc4/data/imx477_noir.json | 429 ++++ src/ipa/rpi/vc4/data/imx477_scientific.json | 479 ++++ src/ipa/rpi/vc4/data/imx477_v1.json | 516 +++++ src/ipa/rpi/vc4/data/imx519.json | 413 ++++ src/ipa/rpi/vc4/data/imx708.json | 556 +++++ src/ipa/rpi/vc4/data/imx708_noir.json | 556 +++++ src/ipa/rpi/vc4/data/imx708_wide.json | 459 ++++ src/ipa/rpi/vc4/data/imx708_wide_noir.json | 459 ++++ src/ipa/rpi/vc4/data/meson.build | 26 + src/ipa/rpi/vc4/data/ov5647.json | 487 ++++ src/ipa/rpi/vc4/data/ov5647_noir.json | 403 ++++ src/ipa/rpi/vc4/data/ov9281_mono.json | 123 + src/ipa/rpi/vc4/data/se327m12.json | 418 ++++ src/ipa/rpi/vc4/data/uncalibrated.json | 118 + src/ipa/rpi/vc4/meson.build | 47 + src/ipa/rpi/vc4/raspberrypi.cpp | 1853 +++++++++++++++ .../pipeline/raspberrypi/data/example.yaml | 46 - .../pipeline/raspberrypi/data/meson.build | 8 - .../pipeline/raspberrypi/delayed_controls.cpp | 293 --- .../pipeline/raspberrypi/delayed_controls.h | 87 - src/libcamera/pipeline/raspberrypi/dma_heaps.cpp | 90 - src/libcamera/pipeline/raspberrypi/dma_heaps.h | 32 - src/libcamera/pipeline/raspberrypi/meson.build | 10 - src/libcamera/pipeline/raspberrypi/raspberrypi.cpp | 2433 -------------------- src/libcamera/pipeline/raspberrypi/rpi_stream.cpp | 250 -- src/libcamera/pipeline/raspberrypi/rpi_stream.h | 185 -- .../pipeline/rpi/common/delayed_controls.cpp | 293 +++ .../pipeline/rpi/common/delayed_controls.h | 87 + src/libcamera/pipeline/rpi/common/meson.build | 6 + src/libcamera/pipeline/rpi/common/rpi_stream.cpp | 250 ++ src/libcamera/pipeline/rpi/common/rpi_stream.h | 185 ++ src/libcamera/pipeline/rpi/meson.build | 12 + src/libcamera/pipeline/rpi/vc4/data/example.yaml | 46 + src/libcamera/pipeline/rpi/vc4/data/meson.build | 8 + src/libcamera/pipeline/rpi/vc4/dma_heaps.cpp | 90 + src/libcamera/pipeline/rpi/vc4/dma_heaps.h | 32 + src/libcamera/pipeline/rpi/vc4/meson.build | 8 + src/libcamera/pipeline/rpi/vc4/raspberrypi.cpp | 2433 ++++++++++++++++++++ 228 files changed, 22780 insertions(+), 22717 deletions(-) delete mode 100644 src/ipa/raspberrypi/README.md delete mode 100644 src/ipa/raspberrypi/cam_helper.cpp delete mode 100644 src/ipa/raspberrypi/cam_helper.h delete mode 100644 src/ipa/raspberrypi/cam_helper_imx219.cpp delete mode 100644 src/ipa/raspberrypi/cam_helper_imx290.cpp delete mode 100644 src/ipa/raspberrypi/cam_helper_imx296.cpp delete mode 100644 src/ipa/raspberrypi/cam_helper_imx477.cpp delete mode 100644 src/ipa/raspberrypi/cam_helper_imx519.cpp delete mode 100644 src/ipa/raspberrypi/cam_helper_imx708.cpp delete mode 100644 src/ipa/raspberrypi/cam_helper_ov5647.cpp delete mode 100644 src/ipa/raspberrypi/cam_helper_ov9281.cpp delete mode 100644 src/ipa/raspberrypi/controller/af_algorithm.h delete mode 100644 src/ipa/raspberrypi/controller/af_status.h delete mode 100644 src/ipa/raspberrypi/controller/agc_algorithm.h delete mode 100644 src/ipa/raspberrypi/controller/agc_status.h delete mode 100644 src/ipa/raspberrypi/controller/algorithm.cpp delete mode 100644 src/ipa/raspberrypi/controller/algorithm.h delete mode 100644 src/ipa/raspberrypi/controller/alsc_status.h delete mode 100644 src/ipa/raspberrypi/controller/awb_algorithm.h delete mode 100644 src/ipa/raspberrypi/controller/awb_status.h delete mode 100644 src/ipa/raspberrypi/controller/black_level_status.h delete mode 100644 src/ipa/raspberrypi/controller/camera_mode.h delete mode 100644 src/ipa/raspberrypi/controller/ccm_algorithm.h delete mode 100644 src/ipa/raspberrypi/controller/ccm_status.h delete mode 100644 src/ipa/raspberrypi/controller/contrast_algorithm.h delete mode 100644 src/ipa/raspberrypi/controller/contrast_status.h delete mode 100644 src/ipa/raspberrypi/controller/controller.cpp delete mode 100644 src/ipa/raspberrypi/controller/controller.h delete mode 100644 src/ipa/raspberrypi/controller/denoise_algorithm.h delete mode 100644 src/ipa/raspberrypi/controller/denoise_status.h delete mode 100644 src/ipa/raspberrypi/controller/device_status.cpp delete mode 100644 src/ipa/raspberrypi/controller/device_status.h delete mode 100644 src/ipa/raspberrypi/controller/dpc_status.h delete mode 100644 src/ipa/raspberrypi/controller/geq_status.h delete mode 100644 src/ipa/raspberrypi/controller/histogram.cpp delete mode 100644 src/ipa/raspberrypi/controller/histogram.h delete mode 100644 src/ipa/raspberrypi/controller/lux_status.h delete mode 100644 src/ipa/raspberrypi/controller/metadata.h delete mode 100644 src/ipa/raspberrypi/controller/noise_status.h delete mode 100644 src/ipa/raspberrypi/controller/pdaf_data.h delete mode 100644 src/ipa/raspberrypi/controller/pwl.cpp delete mode 100644 src/ipa/raspberrypi/controller/pwl.h delete mode 100644 src/ipa/raspberrypi/controller/region_stats.h delete mode 100644 src/ipa/raspberrypi/controller/rpi/af.cpp delete mode 100644 src/ipa/raspberrypi/controller/rpi/af.h delete mode 100644 src/ipa/raspberrypi/controller/rpi/agc.cpp delete mode 100644 src/ipa/raspberrypi/controller/rpi/agc.h delete mode 100644 src/ipa/raspberrypi/controller/rpi/alsc.cpp delete mode 100644 src/ipa/raspberrypi/controller/rpi/alsc.h delete mode 100644 src/ipa/raspberrypi/controller/rpi/awb.cpp delete mode 100644 src/ipa/raspberrypi/controller/rpi/awb.h delete mode 100644 src/ipa/raspberrypi/controller/rpi/black_level.cpp delete mode 100644 src/ipa/raspberrypi/controller/rpi/black_level.h delete mode 100644 src/ipa/raspberrypi/controller/rpi/ccm.cpp delete mode 100644 src/ipa/raspberrypi/controller/rpi/ccm.h delete mode 100644 src/ipa/raspberrypi/controller/rpi/contrast.cpp delete mode 100644 src/ipa/raspberrypi/controller/rpi/contrast.h delete mode 100644 src/ipa/raspberrypi/controller/rpi/dpc.cpp delete mode 100644 src/ipa/raspberrypi/controller/rpi/dpc.h delete mode 100644 src/ipa/raspberrypi/controller/rpi/focus.h delete mode 100644 src/ipa/raspberrypi/controller/rpi/geq.cpp delete mode 100644 src/ipa/raspberrypi/controller/rpi/geq.h delete mode 100644 src/ipa/raspberrypi/controller/rpi/lux.cpp delete mode 100644 src/ipa/raspberrypi/controller/rpi/lux.h delete mode 100644 src/ipa/raspberrypi/controller/rpi/noise.cpp delete mode 100644 src/ipa/raspberrypi/controller/rpi/noise.h delete mode 100644 src/ipa/raspberrypi/controller/rpi/sdn.cpp delete mode 100644 src/ipa/raspberrypi/controller/rpi/sdn.h delete mode 100644 src/ipa/raspberrypi/controller/rpi/sharpen.cpp delete mode 100644 src/ipa/raspberrypi/controller/rpi/sharpen.h delete mode 100644 src/ipa/raspberrypi/controller/sharpen_algorithm.h delete mode 100644 src/ipa/raspberrypi/controller/sharpen_status.h delete mode 100644 src/ipa/raspberrypi/data/imx219.json delete mode 100644 src/ipa/raspberrypi/data/imx219_noir.json delete mode 100644 src/ipa/raspberrypi/data/imx290.json delete mode 100644 src/ipa/raspberrypi/data/imx296.json delete mode 100644 src/ipa/raspberrypi/data/imx296_mono.json delete mode 100644 src/ipa/raspberrypi/data/imx378.json delete mode 100644 src/ipa/raspberrypi/data/imx477.json delete mode 100644 src/ipa/raspberrypi/data/imx477_noir.json delete mode 100644 src/ipa/raspberrypi/data/imx477_scientific.json delete mode 100644 src/ipa/raspberrypi/data/imx477_v1.json delete mode 100644 src/ipa/raspberrypi/data/imx519.json delete mode 100644 src/ipa/raspberrypi/data/imx708.json delete mode 100644 src/ipa/raspberrypi/data/imx708_noir.json delete mode 100644 src/ipa/raspberrypi/data/imx708_wide.json delete mode 100644 src/ipa/raspberrypi/data/imx708_wide_noir.json delete mode 100644 src/ipa/raspberrypi/data/meson.build delete mode 100644 src/ipa/raspberrypi/data/ov5647.json delete mode 100644 src/ipa/raspberrypi/data/ov5647_noir.json delete mode 100644 src/ipa/raspberrypi/data/ov9281_mono.json delete mode 100644 src/ipa/raspberrypi/data/se327m12.json delete mode 100644 src/ipa/raspberrypi/data/uncalibrated.json delete mode 100644 src/ipa/raspberrypi/md_parser.h delete mode 100644 src/ipa/raspberrypi/md_parser_smia.cpp delete mode 100644 src/ipa/raspberrypi/meson.build delete mode 100644 src/ipa/raspberrypi/raspberrypi.cpp delete mode 100644 src/ipa/raspberrypi/statistics.h create mode 100644 src/ipa/rpi/README.md create mode 100644 src/ipa/rpi/cam_helper/cam_helper.cpp create mode 100644 src/ipa/rpi/cam_helper/cam_helper.h create mode 100644 src/ipa/rpi/cam_helper/cam_helper_imx219.cpp create mode 100644 src/ipa/rpi/cam_helper/cam_helper_imx290.cpp create mode 100644 src/ipa/rpi/cam_helper/cam_helper_imx296.cpp create mode 100644 src/ipa/rpi/cam_helper/cam_helper_imx477.cpp create mode 100644 src/ipa/rpi/cam_helper/cam_helper_imx519.cpp create mode 100644 src/ipa/rpi/cam_helper/cam_helper_imx708.cpp create mode 100644 src/ipa/rpi/cam_helper/cam_helper_ov5647.cpp create mode 100644 src/ipa/rpi/cam_helper/cam_helper_ov9281.cpp create mode 100644 src/ipa/rpi/cam_helper/md_parser.h create mode 100644 src/ipa/rpi/cam_helper/md_parser_smia.cpp create mode 100644 src/ipa/rpi/cam_helper/meson.build create mode 100644 src/ipa/rpi/controller/af_algorithm.h create mode 100644 src/ipa/rpi/controller/af_status.h create mode 100644 src/ipa/rpi/controller/agc_algorithm.h create mode 100644 src/ipa/rpi/controller/agc_status.h create mode 100644 src/ipa/rpi/controller/algorithm.cpp create mode 100644 src/ipa/rpi/controller/algorithm.h create mode 100644 src/ipa/rpi/controller/alsc_status.h create mode 100644 src/ipa/rpi/controller/awb_algorithm.h create mode 100644 src/ipa/rpi/controller/awb_status.h create mode 100644 src/ipa/rpi/controller/black_level_status.h create mode 100644 src/ipa/rpi/controller/camera_mode.h create mode 100644 src/ipa/rpi/controller/ccm_algorithm.h create mode 100644 src/ipa/rpi/controller/ccm_status.h create mode 100644 src/ipa/rpi/controller/contrast_algorithm.h create mode 100644 src/ipa/rpi/controller/contrast_status.h create mode 100644 src/ipa/rpi/controller/controller.cpp create mode 100644 src/ipa/rpi/controller/controller.h create mode 100644 src/ipa/rpi/controller/denoise_algorithm.h create mode 100644 src/ipa/rpi/controller/denoise_status.h create mode 100644 src/ipa/rpi/controller/device_status.cpp create mode 100644 src/ipa/rpi/controller/device_status.h create mode 100644 src/ipa/rpi/controller/dpc_status.h create mode 100644 src/ipa/rpi/controller/geq_status.h create mode 100644 src/ipa/rpi/controller/histogram.cpp create mode 100644 src/ipa/rpi/controller/histogram.h create mode 100644 src/ipa/rpi/controller/lux_status.h create mode 100644 src/ipa/rpi/controller/meson.build create mode 100644 src/ipa/rpi/controller/metadata.h create mode 100644 src/ipa/rpi/controller/noise_status.h create mode 100644 src/ipa/rpi/controller/pdaf_data.h create mode 100644 src/ipa/rpi/controller/pwl.cpp create mode 100644 src/ipa/rpi/controller/pwl.h create mode 100644 src/ipa/rpi/controller/region_stats.h create mode 100644 src/ipa/rpi/controller/rpi/af.cpp create mode 100644 src/ipa/rpi/controller/rpi/af.h create mode 100644 src/ipa/rpi/controller/rpi/agc.cpp create mode 100644 src/ipa/rpi/controller/rpi/agc.h create mode 100644 src/ipa/rpi/controller/rpi/alsc.cpp create mode 100644 src/ipa/rpi/controller/rpi/alsc.h create mode 100644 src/ipa/rpi/controller/rpi/awb.cpp create mode 100644 src/ipa/rpi/controller/rpi/awb.h create mode 100644 src/ipa/rpi/controller/rpi/black_level.cpp create mode 100644 src/ipa/rpi/controller/rpi/black_level.h create mode 100644 src/ipa/rpi/controller/rpi/ccm.cpp create mode 100644 src/ipa/rpi/controller/rpi/ccm.h create mode 100644 src/ipa/rpi/controller/rpi/contrast.cpp create mode 100644 src/ipa/rpi/controller/rpi/contrast.h create mode 100644 src/ipa/rpi/controller/rpi/dpc.cpp create mode 100644 src/ipa/rpi/controller/rpi/dpc.h create mode 100644 src/ipa/rpi/controller/rpi/focus.h create mode 100644 src/ipa/rpi/controller/rpi/geq.cpp create mode 100644 src/ipa/rpi/controller/rpi/geq.h create mode 100644 src/ipa/rpi/controller/rpi/lux.cpp create mode 100644 src/ipa/rpi/controller/rpi/lux.h create mode 100644 src/ipa/rpi/controller/rpi/noise.cpp create mode 100644 src/ipa/rpi/controller/rpi/noise.h create mode 100644 src/ipa/rpi/controller/rpi/sdn.cpp create mode 100644 src/ipa/rpi/controller/rpi/sdn.h create mode 100644 src/ipa/rpi/controller/rpi/sharpen.cpp create mode 100644 src/ipa/rpi/controller/rpi/sharpen.h create mode 100644 src/ipa/rpi/controller/sharpen_algorithm.h create mode 100644 src/ipa/rpi/controller/sharpen_status.h create mode 100644 src/ipa/rpi/controller/statistics.h create mode 100644 src/ipa/rpi/meson.build create mode 100644 src/ipa/rpi/vc4/data/imx219.json create mode 100644 src/ipa/rpi/vc4/data/imx219_noir.json create mode 100644 src/ipa/rpi/vc4/data/imx290.json create mode 100644 src/ipa/rpi/vc4/data/imx296.json create mode 100644 src/ipa/rpi/vc4/data/imx296_mono.json create mode 100644 src/ipa/rpi/vc4/data/imx378.json create mode 100644 src/ipa/rpi/vc4/data/imx477.json create mode 100644 src/ipa/rpi/vc4/data/imx477_noir.json create mode 100644 src/ipa/rpi/vc4/data/imx477_scientific.json create mode 100644 src/ipa/rpi/vc4/data/imx477_v1.json create mode 100644 src/ipa/rpi/vc4/data/imx519.json create mode 100644 src/ipa/rpi/vc4/data/imx708.json create mode 100644 src/ipa/rpi/vc4/data/imx708_noir.json create mode 100644 src/ipa/rpi/vc4/data/imx708_wide.json create mode 100644 src/ipa/rpi/vc4/data/imx708_wide_noir.json create mode 100644 src/ipa/rpi/vc4/data/meson.build create mode 100644 src/ipa/rpi/vc4/data/ov5647.json create mode 100644 src/ipa/rpi/vc4/data/ov5647_noir.json create mode 100644 src/ipa/rpi/vc4/data/ov9281_mono.json create mode 100644 src/ipa/rpi/vc4/data/se327m12.json create mode 100644 src/ipa/rpi/vc4/data/uncalibrated.json create mode 100644 src/ipa/rpi/vc4/meson.build create mode 100644 src/ipa/rpi/vc4/raspberrypi.cpp delete mode 100644 src/libcamera/pipeline/raspberrypi/data/example.yaml delete mode 100644 src/libcamera/pipeline/raspberrypi/data/meson.build delete mode 100644 src/libcamera/pipeline/raspberrypi/delayed_controls.cpp delete mode 100644 src/libcamera/pipeline/raspberrypi/delayed_controls.h delete mode 100644 src/libcamera/pipeline/raspberrypi/dma_heaps.cpp delete mode 100644 src/libcamera/pipeline/raspberrypi/dma_heaps.h delete mode 100644 src/libcamera/pipeline/raspberrypi/meson.build delete mode 100644 src/libcamera/pipeline/raspberrypi/raspberrypi.cpp delete mode 100644 src/libcamera/pipeline/raspberrypi/rpi_stream.cpp delete mode 100644 src/libcamera/pipeline/raspberrypi/rpi_stream.h create mode 100644 src/libcamera/pipeline/rpi/common/delayed_controls.cpp create mode 100644 src/libcamera/pipeline/rpi/common/delayed_controls.h create mode 100644 src/libcamera/pipeline/rpi/common/meson.build create mode 100644 src/libcamera/pipeline/rpi/common/rpi_stream.cpp create mode 100644 src/libcamera/pipeline/rpi/common/rpi_stream.h create mode 100644 src/libcamera/pipeline/rpi/meson.build create mode 100644 src/libcamera/pipeline/rpi/vc4/data/example.yaml create mode 100644 src/libcamera/pipeline/rpi/vc4/data/meson.build create mode 100644 src/libcamera/pipeline/rpi/vc4/dma_heaps.cpp create mode 100644 src/libcamera/pipeline/rpi/vc4/dma_heaps.h create mode 100644 src/libcamera/pipeline/rpi/vc4/meson.build create mode 100644 src/libcamera/pipeline/rpi/vc4/raspberrypi.cpp diff --git a/.reuse/dep5 b/.reuse/dep5 index bba46179..d283a0c9 100644 --- a/.reuse/dep5 +++ b/.reuse/dep5 @@ -7,7 +7,7 @@ Files: Documentation/theme/static/search.png Copyright: 2022 Fonticons, Inc. License: CC-BY-4.0 -Files: src/ipa/raspberrypi/data/*.json +Files: src/ipa/rpi/vc4/data/*.json utils/raspberrypi/ctt/ctt_config_example.json utils/raspberrypi/ctt/ctt_ref.pgm Copyright: 2019-2020 Raspberry Pi Ltd diff --git a/Documentation/environment_variables.rst b/Documentation/environment_variables.rst index ceeb251a..a9b230bc 100644 --- a/Documentation/environment_variables.rst +++ b/Documentation/environment_variables.rst @@ -40,7 +40,7 @@ LIBCAMERA_IPA_MODULE_PATH LIBCAMERA_RPI_CONFIG_FILE Define a custom configuration file to use in the Raspberry Pi pipeline handler. - Example value: ``/usr/local/share/libcamera/pipeline/raspberrypi/minimal_mem.yaml`` + Example value: ``/usr/local/share/libcamera/pipeline/rpi/vc4/minimal_mem.yaml`` Further details --------------- @@ -143,7 +143,7 @@ contain tuning parameters for the algorithms, in JSON format. The ``LIBCAMERA_IPA_CONFIG_PATH`` variable can be used to specify custom storage locations to search for those configuration files. -`Examples `__ +`Examples `__ IPA module ~~~~~~~~~~ diff --git a/Documentation/guides/introduction.rst b/Documentation/guides/introduction.rst index 2d1760c1..700ec2d3 100644 --- a/Documentation/guides/introduction.rst +++ b/Documentation/guides/introduction.rst @@ -288,7 +288,7 @@ with dedicated pipeline handlers: - Intel IPU3 (ipu3) - Rockchip RK3399 (rkisp1) - - RaspberryPi 3 and 4 (raspberrypi) + - RaspberryPi 3 and 4 (rpi/vc4) Furthermore, generic platform support is provided for the following: diff --git a/Documentation/guides/pipeline-handler.rst b/Documentation/guides/pipeline-handler.rst index 1acd1812..57644534 100644 --- a/Documentation/guides/pipeline-handler.rst +++ b/Documentation/guides/pipeline-handler.rst @@ -183,7 +183,7 @@ to the libcamera build options in the top level ``meson_options.txt``. option('pipelines', type : 'array', - choices : ['ipu3', 'raspberrypi', 'rkisp1', 'simple', 'uvcvideo', 'vimc', 'vivid'], + choices : ['ipu3', 'rkisp1', 'rpi/vc4', 'simple', 'uvcvideo', 'vimc', 'vivid'], description : 'Select which pipeline handlers to include') diff --git a/include/libcamera/ipa/meson.build b/include/libcamera/ipa/meson.build index d2bb2cf4..6a5ae686 100644 --- a/include/libcamera/ipa/meson.build +++ b/include/libcamera/ipa/meson.build @@ -64,7 +64,7 @@ libcamera_generated_ipa_headers += custom_target('core_ipa_serializer_h', pipeline_ipa_mojom_mapping = { 'ipu3': 'ipu3.mojom', 'rkisp1': 'rkisp1.mojom', - 'raspberrypi': 'raspberrypi.mojom', + 'rpi/vc4': 'raspberrypi.mojom', 'vimc': 'vimc.mojom', } diff --git a/meson.build b/meson.build index 2d99029b..e1fd9243 100644 --- a/meson.build +++ b/meson.build @@ -194,8 +194,8 @@ arch_x86 = ['x86', 'x86_64'] pipelines_support = { 'imx8-isi': arch_arm, 'ipu3': arch_x86, - 'raspberrypi': arch_arm, 'rkisp1': arch_arm, + 'rpi/vc4': arch_arm, 'simple': arch_arm, 'uvcvideo': ['any'], 'vimc': ['test'], diff --git a/meson_options.txt b/meson_options.txt index 78a78b72..c8cd53b4 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -27,7 +27,7 @@ option('gstreamer', option('ipas', type : 'array', - choices : ['ipu3', 'raspberrypi', 'rkisp1', 'vimc'], + choices : ['ipu3', 'rkisp1', 'rpi/vc4', 'vimc'], description : 'Select which IPA modules to build') option('lc-compliance', @@ -43,8 +43,8 @@ option('pipelines', 'auto', 'imx8-isi', 'ipu3', - 'raspberrypi', 'rkisp1', + 'rpi/vc4', 'simple', 'uvcvideo', 'vimc' diff --git a/src/ipa/raspberrypi/README.md b/src/ipa/raspberrypi/README.md deleted file mode 100644 index 94a8ccc8..00000000 --- a/src/ipa/raspberrypi/README.md +++ /dev/null @@ -1,25 +0,0 @@ -.. SPDX-License-Identifier: BSD-2-Clause - -# _libcamera_ for the Raspberry Pi - -Raspberry Pi provides a fully featured pipeline handler and control algorithms -(IPAs, or "Image Processing Algorithms") to work with _libcamera_. Support is -included for all existing Raspberry Pi camera modules. - -_libcamera_ for the Raspberry Pi allows users to: - -1. Use their existing Raspberry Pi cameras. -1. Change the tuning of the image processing for their Raspberry Pi cameras. -1. Alter or amend the control algorithms (such as AGC/AEC, AWB or any others) - that control the sensor and ISP. -1. Implement their own custom control algorithms. -1. Supply new tunings and/or algorithms for completely new sensors. - -## How to install and run _libcamera_ on the Raspberry Pi - -Please follow the instructions [here](https://www.raspberrypi.com/documentation/accessories/camera.html). - -## Documentation - -Full documentation for the _Raspberry Pi Camera Algorithm and Tuning Guide_ can -be found [here](https://datasheets.raspberrypi.com/camera/raspberry-pi-camera-guide.pdf). diff --git a/src/ipa/raspberrypi/cam_helper.cpp b/src/ipa/raspberrypi/cam_helper.cpp deleted file mode 100644 index ddd5e9a4..00000000 --- a/src/ipa/raspberrypi/cam_helper.cpp +++ /dev/null @@ -1,265 +0,0 @@ -/* SPDX-License-Identifier: BSD-2-Clause */ -/* - * Copyright (C) 2019, Raspberry Pi Ltd - * - * cam_helper.cpp - helper information for different sensors - */ - -#include - -#include -#include -#include - -#include "libcamera/internal/v4l2_videodevice.h" - -#include "cam_helper.h" -#include "md_parser.h" - -using namespace RPiController; -using namespace libcamera; -using libcamera::utils::Duration; -using namespace std::literals::chrono_literals; - -namespace libcamera { -LOG_DECLARE_CATEGORY(IPARPI) -} - -namespace { - -std::map &camHelpers() -{ - static std::map helpers; - return helpers; -} - -} /* namespace */ - -CamHelper *CamHelper::create(std::string const &camName) -{ - /* - * CamHelpers get registered by static RegisterCamHelper - * initialisers. - */ - for (auto &p : camHelpers()) { - if (camName.find(p.first) != std::string::npos) - return p.second(); - } - - return nullptr; -} - -CamHelper::CamHelper(std::unique_ptr parser, unsigned int frameIntegrationDiff) - : parser_(std::move(parser)), frameIntegrationDiff_(frameIntegrationDiff) -{ -} - -CamHelper::~CamHelper() -{ -} - -void CamHelper::prepare(Span buffer, - Metadata &metadata) -{ - parseEmbeddedData(buffer, metadata); -} - -void CamHelper::process([[maybe_unused]] StatisticsPtr &stats, - [[maybe_unused]] Metadata &metadata) -{ -} - -uint32_t CamHelper::exposureLines(const Duration exposure, const Duration lineLength) const -{ - return exposure / lineLength; -} - -Duration CamHelper::exposure(uint32_t exposureLines, const Duration lineLength) const -{ - return exposureLines * lineLength; -} - -std::pair CamHelper::getBlanking(Duration &exposure, - Duration minFrameDuration, - Duration maxFrameDuration) const -{ - uint32_t frameLengthMin, frameLengthMax, vblank, hblank; - Duration lineLength = mode_.minLineLength; - - /* - * minFrameDuration and maxFrameDuration are clamped by the caller - * based on the limits for the active sensor mode. - * - * frameLengthMax gets calculated on the smallest line length as we do - * not want to extend that unless absolutely necessary. - */ - frameLengthMin = minFrameDuration / mode_.minLineLength; - frameLengthMax = maxFrameDuration / mode_.minLineLength; - - /* - * Watch out for (exposureLines + frameIntegrationDiff_) overflowing a - * uint32_t in the std::clamp() below when the exposure time is - * extremely (extremely!) long - as happens when the IPA calculates the - * maximum possible exposure time. - */ - uint32_t exposureLines = std::min(CamHelper::exposureLines(exposure, lineLength), - std::numeric_limits::max() - frameIntegrationDiff_); - uint32_t frameLengthLines = std::clamp(exposureLines + frameIntegrationDiff_, - frameLengthMin, frameLengthMax); - - /* - * If our frame length lines is above the maximum allowed, see if we can - * extend the line length to accommodate the requested frame length. - */ - if (frameLengthLines > mode_.maxFrameLength) { - Duration lineLengthAdjusted = lineLength * frameLengthLines / mode_.maxFrameLength; - lineLength = std::min(mode_.maxLineLength, lineLengthAdjusted); - frameLengthLines = mode_.maxFrameLength; - } - - hblank = lineLengthToHblank(lineLength); - vblank = frameLengthLines - mode_.height; - - /* - * Limit the exposure to the maximum frame duration requested, and - * re-calculate if it has been clipped. - */ - exposureLines = std::min(frameLengthLines - frameIntegrationDiff_, - CamHelper::exposureLines(exposure, lineLength)); - exposure = CamHelper::exposure(exposureLines, lineLength); - - return { vblank, hblank }; -} - -Duration CamHelper::hblankToLineLength(uint32_t hblank) const -{ - return (mode_.width + hblank) * (1.0s / mode_.pixelRate); -} - -uint32_t CamHelper::lineLengthToHblank(const Duration &lineLength) const -{ - return (lineLength * mode_.pixelRate / 1.0s) - mode_.width; -} - -Duration CamHelper::lineLengthPckToDuration(uint32_t lineLengthPck) const -{ - return lineLengthPck * (1.0s / mode_.pixelRate); -} - -void CamHelper::setCameraMode(const CameraMode &mode) -{ - mode_ = mode; - if (parser_) { - parser_->reset(); - parser_->setBitsPerPixel(mode.bitdepth); - parser_->setLineLengthBytes(0); /* We use SetBufferSize. */ - } -} - -void CamHelper::getDelays(int &exposureDelay, int &gainDelay, - int &vblankDelay, int &hblankDelay) const -{ - /* - * These values are correct for many sensors. Other sensors will - * need to over-ride this function. - */ - exposureDelay = 2; - gainDelay = 1; - vblankDelay = 2; - hblankDelay = 2; -} - -bool CamHelper::sensorEmbeddedDataPresent() const -{ - return false; -} - -double CamHelper::getModeSensitivity([[maybe_unused]] const CameraMode &mode) const -{ - /* - * Most sensors have the same sensitivity in every mode, but this - * function can be overridden for those that do not. Note that it is - * called before mode_ is set, so it must return the sensitivity - * of the mode that is passed in. - */ - return 1.0; -} - -unsigned int CamHelper::hideFramesStartup() const -{ - /* - * The number of frames when a camera first starts that shouldn't be - * displayed as they are invalid in some way. - */ - return 0; -} - -unsigned int CamHelper::hideFramesModeSwitch() const -{ - /* After a mode switch, many sensors return valid frames immediately. */ - return 0; -} - -unsigned int CamHelper::mistrustFramesStartup() const -{ - /* Many sensors return a single bad frame on start-up. */ - return 1; -} - -unsigned int CamHelper::mistrustFramesModeSwitch() const -{ - /* Many sensors return valid metadata immediately. */ - return 0; -} - -void CamHelper::parseEmbeddedData(Span buffer, - Metadata &metadata) -{ - MdParser::RegisterMap registers; - Metadata parsedMetadata; - - if (buffer.empty()) - return; - - if (parser_->parse(buffer, registers) != MdParser::Status::OK) { - LOG(IPARPI, Error) << "Embedded data buffer parsing failed"; - return; - } - - populateMetadata(registers, parsedMetadata); - metadata.merge(parsedMetadata); - - /* - * Overwrite the exposure/gain, line/frame length and sensor temperature values - * in the existing DeviceStatus with values from the parsed embedded buffer. - * Fetch it first in case any other fields were set meaningfully. - */ - DeviceStatus deviceStatus, parsedDeviceStatus; - if (metadata.get("device.status", deviceStatus) || - parsedMetadata.get("device.status", parsedDeviceStatus)) { - LOG(IPARPI, Error) << "DeviceStatus not found"; - return; - } - - deviceStatus.shutterSpeed = parsedDeviceStatus.shutterSpeed; - deviceStatus.analogueGain = parsedDeviceStatus.analogueGain; - deviceStatus.frameLength = parsedDeviceStatus.frameLength; - deviceStatus.lineLength = parsedDeviceStatus.lineLength; - if (parsedDeviceStatus.sensorTemperature) - deviceStatus.sensorTemperature = parsedDeviceStatus.sensorTemperature; - - LOG(IPARPI, Debug) << "Metadata updated - " << deviceStatus; - - metadata.set("device.status", deviceStatus); -} - -void CamHelper::populateMetadata([[maybe_unused]] const MdParser::RegisterMap ®isters, - [[maybe_unused]] Metadata &metadata) const -{ -} - -RegisterCamHelper::RegisterCamHelper(char const *camName, - CamHelperCreateFunc createFunc) -{ - camHelpers()[std::string(camName)] = createFunc; -} diff --git a/src/ipa/raspberrypi/cam_helper.h b/src/ipa/raspberrypi/cam_helper.h deleted file mode 100644 index b3f8c980..00000000 --- a/src/ipa/raspberrypi/cam_helper.h +++ /dev/null @@ -1,132 +0,0 @@ -/* SPDX-License-Identifier: BSD-2-Clause */ -/* - * Copyright (C) 2019, Raspberry Pi Ltd - * - * cam_helper.h - helper class providing camera information - */ -#pragma once - -#include -#include -#include - -#include -#include - -#include "camera_mode.h" -#include "controller/controller.h" -#include "controller/metadata.h" -#include "md_parser.h" - -#include "libcamera/internal/v4l2_videodevice.h" - -namespace RPiController { - -/* - * The CamHelper class provides a number of facilities that anyone trying - * to drive a camera will need to know, but which are not provided by the - * standard driver framework. Specifically, it provides: - * - * A "CameraMode" structure to describe extra information about the chosen - * mode of the driver. For example, how it is cropped from the full sensor - * area, how it is scaled, whether pixels are averaged compared to the full - * resolution. - * - * The ability to convert between number of lines of exposure and actual - * exposure time, and to convert between the sensor's gain codes and actual - * gains. - * - * A function to return the number of frames of delay between updating exposure, - * analogue gain and vblanking, and for the changes to take effect. For many - * sensors these take the values 2, 1 and 2 respectively, but sensors that are - * different will need to over-ride the default function provided. - * - * A function to query if the sensor outputs embedded data that can be parsed. - * - * A function to return the sensitivity of a given camera mode. - * - * A parser to parse the embedded data buffers provided by some sensors (for - * example, the imx219 does; the ov5647 doesn't). This allows us to know for - * sure the exposure and gain of the frame we're looking at. CamHelper - * provides functions for converting analogue gains to and from the sensor's - * native gain codes. - * - * Finally, a set of functions that determine how to handle the vagaries of - * different camera modules on start-up or when switching modes. Some - * modules may produce one or more frames that are not yet correctly exposed, - * or where the metadata may be suspect. We have the following functions: - * HideFramesStartup(): Tell the pipeline handler not to return this many - * frames at start-up. This can also be used to hide initial frames - * while the AGC and other algorithms are sorting themselves out. - * HideFramesModeSwitch(): Tell the pipeline handler not to return this - * many frames after a mode switch (other than start-up). Some sensors - * may produce innvalid frames after a mode switch; others may not. - * MistrustFramesStartup(): At start-up a sensor may return frames for - * which we should not run any control algorithms (for example, metadata - * may be invalid). - * MistrustFramesModeSwitch(): The number of frames, after a mode switch - * (other than start-up), for which control algorithms should not run - * (for example, metadata may be unreliable). - */ - -class CamHelper -{ -public: - static CamHelper *create(std::string const &camName); - CamHelper(std::unique_ptr parser, unsigned int frameIntegrationDiff); - virtual ~CamHelper(); - void setCameraMode(const CameraMode &mode); - virtual void prepare(libcamera::Span buffer, - Metadata &metadata); - virtual void process(StatisticsPtr &stats, Metadata &metadata); - virtual uint32_t exposureLines(const libcamera::utils::Duration exposure, - const libcamera::utils::Duration lineLength) const; - virtual libcamera::utils::Duration exposure(uint32_t exposureLines, - const libcamera::utils::Duration lineLength) const; - virtual std::pair getBlanking(libcamera::utils::Duration &exposure, - libcamera::utils::Duration minFrameDuration, - libcamera::utils::Duration maxFrameDuration) const; - libcamera::utils::Duration hblankToLineLength(uint32_t hblank) const; - uint32_t lineLengthToHblank(const libcamera::utils::Duration &duration) const; - libcamera::utils::Duration lineLengthPckToDuration(uint32_t lineLengthPck) const; - virtual uint32_t gainCode(double gain) const = 0; - virtual double gain(uint32_t gainCode) const = 0; - virtual void getDelays(int &exposureDelay, int &gainDelay, - int &vblankDelay, int &hblankDelay) const; - virtual bool sensorEmbeddedDataPresent() const; - virtual double getModeSensitivity(const CameraMode &mode) const; - virtual unsigned int hideFramesStartup() const; - virtual unsigned int hideFramesModeSwitch() const; - virtual unsigned int mistrustFramesStartup() const; - virtual unsigned int mistrustFramesModeSwitch() const; - -protected: - void parseEmbeddedData(libcamera::Span buffer, - Metadata &metadata); - virtual void populateMetadata(const MdParser::RegisterMap ®isters, - Metadata &metadata) const; - - std::unique_ptr parser_; - CameraMode mode_; - -private: - /* - * Smallest difference between the frame length and integration time, - * in units of lines. - */ - unsigned int frameIntegrationDiff_; -}; - -/* - * This is for registering camera helpers with the system, so that the - * CamHelper::Create function picks them up automatically. - */ - -typedef CamHelper *(*CamHelperCreateFunc)(); -struct RegisterCamHelper -{ - RegisterCamHelper(char const *camName, - CamHelperCreateFunc createFunc); -}; - -} /* namespace RPi */ diff --git a/src/ipa/raspberrypi/cam_helper_imx219.cpp b/src/ipa/raspberrypi/cam_helper_imx219.cpp deleted file mode 100644 index c3337ed0..00000000 --- a/src/ipa/raspberrypi/cam_helper_imx219.cpp +++ /dev/null @@ -1,115 +0,0 @@ -/* SPDX-License-Identifier: BSD-2-Clause */ -/* - * Copyright (C) 2019, Raspberry Pi Ltd - * - * cam_helper_imx219.cpp - camera helper for imx219 sensor - */ - -#include -#include -#include -#include - -/* - * We have observed that the imx219 embedded data stream randomly returns junk - * register values. Do not rely on embedded data until this has been resolved. - */ -#define ENABLE_EMBEDDED_DATA 0 - -#include "cam_helper.h" -#if ENABLE_EMBEDDED_DATA -#include "md_parser.h" -#endif - -using namespace RPiController; - -/* - * We care about one gain register and a pair of exposure registers. Their I2C - * addresses from the Sony IMX219 datasheet: - */ -constexpr uint32_t gainReg = 0x157; -constexpr uint32_t expHiReg = 0x15a; -constexpr uint32_t expLoReg = 0x15b; -constexpr uint32_t frameLengthHiReg = 0x160; -constexpr uint32_t frameLengthLoReg = 0x161; -constexpr uint32_t lineLengthHiReg = 0x162; -constexpr uint32_t lineLengthLoReg = 0x163; -constexpr std::initializer_list registerList [[maybe_unused]] - = { expHiReg, expLoReg, gainReg, frameLengthHiReg, frameLengthLoReg, - lineLengthHiReg, lineLengthLoReg }; - -class CamHelperImx219 : public CamHelper -{ -public: - CamHelperImx219(); - uint32_t gainCode(double gain) const override; - double gain(uint32_t gainCode) const override; - unsigned int mistrustFramesModeSwitch() const override; - bool sensorEmbeddedDataPresent() const override; - -private: - /* - * Smallest difference between the frame length and integration time, - * in units of lines. - */ - static constexpr int frameIntegrationDiff = 4; - - void populateMetadata(const MdParser::RegisterMap ®isters, - Metadata &metadata) const override; -}; - -CamHelperImx219::CamHelperImx219() -#if ENABLE_EMBEDDED_DATA - : CamHelper(std::make_unique(registerList), frameIntegrationDiff) -#else - : CamHelper({}, frameIntegrationDiff) -#endif -{ -} - -uint32_t CamHelperImx219::gainCode(double gain) const -{ - return (uint32_t)(256 - 256 / gain); -} - -double CamHelperImx219::gain(uint32_t gainCode) const -{ - return 256.0 / (256 - gainCode); -} - -unsigned int CamHelperImx219::mistrustFramesModeSwitch() const -{ - /* - * For reasons unknown, we do occasionally get a bogus metadata frame - * at a mode switch (though not at start-up). Possibly warrants some - * investigation, though not a big deal. - */ - return 1; -} - -bool CamHelperImx219::sensorEmbeddedDataPresent() const -{ - return ENABLE_EMBEDDED_DATA; -} - -void CamHelperImx219::populateMetadata(const MdParser::RegisterMap ®isters, - Metadata &metadata) const -{ - DeviceStatus deviceStatus; - - deviceStatus.lineLength = lineLengthPckToDuration(registers.at(lineLengthHiReg) * 256 + - registers.at(lineLengthLoReg)); - deviceStatus.shutterSpeed = exposure(registers.at(expHiReg) * 256 + registers.at(expLoReg), - deviceStatus.lineLength); - deviceStatus.analogueGain = gain(registers.at(gainReg)); - deviceStatus.frameLength = registers.at(frameLengthHiReg) * 256 + registers.at(frameLengthLoReg); - - metadata.set("device.status", deviceStatus); -} - -static CamHelper *create() -{ - return new CamHelperImx219(); -} - -static RegisterCamHelper reg("imx219", &create); diff --git a/src/ipa/raspberrypi/cam_helper_imx290.cpp b/src/ipa/raspberrypi/cam_helper_imx290.cpp deleted file mode 100644 index 7d6f5b54..00000000 --- a/src/ipa/raspberrypi/cam_helper_imx290.cpp +++ /dev/null @@ -1,68 +0,0 @@ -/* SPDX-License-Identifier: BSD-2-Clause */ -/* - * Copyright (C) 2021, Raspberry Pi Ltd - * - * cam_helper_imx290.cpp - camera helper for imx290 sensor - */ - -#include - -#include "cam_helper.h" - -using namespace RPiController; - -class CamHelperImx290 : public CamHelper -{ -public: - CamHelperImx290(); - uint32_t gainCode(double gain) const override; - double gain(uint32_t gainCode) const override; - void getDelays(int &exposureDelay, int &gainDelay, - int &vblankDelay, int &hblankDelay) const override; - unsigned int hideFramesModeSwitch() const override; - -private: - /* - * Smallest difference between the frame length and integration time, - * in units of lines. - */ - static constexpr int frameIntegrationDiff = 2; -}; - -CamHelperImx290::CamHelperImx290() - : CamHelper({}, frameIntegrationDiff) -{ -} - -uint32_t CamHelperImx290::gainCode(double gain) const -{ - int code = 66.6667 * log10(gain); - return std::max(0, std::min(code, 0xf0)); -} - -double CamHelperImx290::gain(uint32_t gainCode) const -{ - return pow(10, 0.015 * gainCode); -} - -void CamHelperImx290::getDelays(int &exposureDelay, int &gainDelay, - int &vblankDelay, int &hblankDelay) const -{ - exposureDelay = 2; - gainDelay = 2; - vblankDelay = 2; - hblankDelay = 2; -} - -unsigned int CamHelperImx290::hideFramesModeSwitch() const -{ - /* After a mode switch, we seem to get 1 bad frame. */ - return 1; -} - -static CamHelper *create() -{ - return new CamHelperImx290(); -} - -static RegisterCamHelper reg("imx290", &create); diff --git a/src/ipa/raspberrypi/cam_helper_imx296.cpp b/src/ipa/raspberrypi/cam_helper_imx296.cpp deleted file mode 100644 index ecb845e7..00000000 --- a/src/ipa/raspberrypi/cam_helper_imx296.cpp +++ /dev/null @@ -1,83 +0,0 @@ -/* SPDX-License-Identifier: BSD-2-Clause */ -/* - * Copyright (C) 2020, Raspberry Pi Ltd - * - * cam_helper_imx296.cpp - Camera helper for IMX296 sensor - */ - -#include -#include -#include - -#include "cam_helper.h" - -using namespace RPiController; -using libcamera::utils::Duration; -using namespace std::literals::chrono_literals; - -class CamHelperImx296 : public CamHelper -{ -public: - CamHelperImx296(); - uint32_t gainCode(double gain) const override; - double gain(uint32_t gainCode) const override; - uint32_t exposureLines(const Duration exposure, const Duration lineLength) const override; - Duration exposure(uint32_t exposureLines, const Duration lineLength) const override; - void getDelays(int &exposureDelay, int &gainDelay, - int &vblankDelay, int &hblankDelay) const override; - -private: - static constexpr uint32_t minExposureLines = 1; - static constexpr uint32_t maxGainCode = 239; - static constexpr Duration timePerLine = 550.0 / 37.125e6 * 1.0s; - - /* - * Smallest difference between the frame length and integration time, - * in units of lines. - */ - static constexpr int frameIntegrationDiff = 4; -}; - -CamHelperImx296::CamHelperImx296() - : CamHelper(nullptr, frameIntegrationDiff) -{ -} - -uint32_t CamHelperImx296::gainCode(double gain) const -{ - uint32_t code = 20 * std::log10(gain) * 10; - return std::min(code, maxGainCode); -} - -double CamHelperImx296::gain(uint32_t gainCode) const -{ - return std::pow(10.0, gainCode / 200.0); -} - -uint32_t CamHelperImx296::exposureLines(const Duration exposure, - [[maybe_unused]] const Duration lineLength) const -{ - return std::max(minExposureLines, (exposure - 14.26us) / timePerLine); -} - -Duration CamHelperImx296::exposure(uint32_t exposureLines, - [[maybe_unused]] const Duration lineLength) const -{ - return std::max(minExposureLines, exposureLines) * timePerLine + 14.26us; -} - -void CamHelperImx296::getDelays(int &exposureDelay, int &gainDelay, - int &vblankDelay, int &hblankDelay) const -{ - exposureDelay = 2; - gainDelay = 2; - vblankDelay = 2; - hblankDelay = 2; -} - -static CamHelper *create() -{ - return new CamHelperImx296(); -} - -static RegisterCamHelper reg("imx296", &create); diff --git a/src/ipa/raspberrypi/cam_helper_imx477.cpp b/src/ipa/raspberrypi/cam_helper_imx477.cpp deleted file mode 100644 index bc769ca7..00000000 --- a/src/ipa/raspberrypi/cam_helper_imx477.cpp +++ /dev/null @@ -1,197 +0,0 @@ -/* SPDX-License-Identifier: BSD-2-Clause */ -/* - * Copyright (C) 2020, Raspberry Pi Ltd - * - * cam_helper_imx477.cpp - camera helper for imx477 sensor - */ - -#include -#include -#include -#include -#include -#include - -#include - -#include "cam_helper.h" -#include "md_parser.h" - -using namespace RPiController; -using namespace libcamera; -using libcamera::utils::Duration; - -namespace libcamera { -LOG_DECLARE_CATEGORY(IPARPI) -} - -/* - * We care about two gain registers and a pair of exposure registers. Their - * I2C addresses from the Sony IMX477 datasheet: - */ -constexpr uint32_t expHiReg = 0x0202; -constexpr uint32_t expLoReg = 0x0203; -constexpr uint32_t gainHiReg = 0x0204; -constexpr uint32_t gainLoReg = 0x0205; -constexpr uint32_t frameLengthHiReg = 0x0340; -constexpr uint32_t frameLengthLoReg = 0x0341; -constexpr uint32_t lineLengthHiReg = 0x0342; -constexpr uint32_t lineLengthLoReg = 0x0343; -constexpr uint32_t temperatureReg = 0x013a; -constexpr std::initializer_list registerList = - { expHiReg, expLoReg, gainHiReg, gainLoReg, frameLengthHiReg, frameLengthLoReg, - lineLengthHiReg, lineLengthLoReg, temperatureReg }; - -class CamHelperImx477 : public CamHelper -{ -public: - CamHelperImx477(); - uint32_t gainCode(double gain) const override; - double gain(uint32_t gainCode) const override; - void prepare(libcamera::Span buffer, Metadata &metadata) override; - std::pair getBlanking(Duration &exposure, Duration minFrameDuration, - Duration maxFrameDuration) const override; - void getDelays(int &exposureDelay, int &gainDelay, - int &vblankDelay, int &hblankDelay) const override; - bool sensorEmbeddedDataPresent() const override; - -private: - /* - * Smallest difference between the frame length and integration time, - * in units of lines. - */ - static constexpr int frameIntegrationDiff = 22; - /* Maximum frame length allowable for long exposure calculations. */ - static constexpr int frameLengthMax = 0xffdc; - /* Largest long exposure scale factor given as a left shift on the frame length. */ - static constexpr int longExposureShiftMax = 7; - - void populateMetadata(const MdParser::RegisterMap ®isters, - Metadata &metadata) const override; -}; - -CamHelperImx477::CamHelperImx477() - : CamHelper(std::make_unique(registerList), frameIntegrationDiff) -{ -} - -uint32_t CamHelperImx477::gainCode(double gain) const -{ - return static_cast(1024 - 1024 / gain); -} - -double CamHelperImx477::gain(uint32_t gainCode) const -{ - return 1024.0 / (1024 - gainCode); -} - -void CamHelperImx477::prepare(libcamera::Span buffer, Metadata &metadata) -{ - MdParser::RegisterMap registers; - DeviceStatus deviceStatus; - - if (metadata.get("device.status", deviceStatus)) { - LOG(IPARPI, Error) << "DeviceStatus not found from DelayedControls"; - return; - } - - parseEmbeddedData(buffer, metadata); - - /* - * The DeviceStatus struct is first populated with values obtained from - * DelayedControls. If this reports frame length is > frameLengthMax, - * it means we are using a long exposure mode. Since the long exposure - * scale factor is not returned back through embedded data, we must rely - * on the existing exposure lines and frame length values returned by - * DelayedControls. - * - * Otherwise, all values are updated with what is reported in the - * embedded data. - */ - if (deviceStatus.frameLength > frameLengthMax) { - DeviceStatus parsedDeviceStatus; - - metadata.get("device.status", parsedDeviceStatus); - parsedDeviceStatus.shutterSpeed = deviceStatus.shutterSpeed; - parsedDeviceStatus.frameLength = deviceStatus.frameLength; - metadata.set("device.status", parsedDeviceStatus); - - LOG(IPARPI, Debug) << "Metadata updated for long exposure: " - << parsedDeviceStatus; - } -} - -std::pair CamHelperImx477::getBlanking(Duration &exposure, - Duration minFrameDuration, - Duration maxFrameDuration) const -{ - uint32_t frameLength, exposureLines; - unsigned int shift = 0; - - auto [vblank, hblank] = CamHelper::getBlanking(exposure, minFrameDuration, - maxFrameDuration); - - frameLength = mode_.height + vblank; - Duration lineLength = hblankToLineLength(hblank); - - /* - * Check if the frame length calculated needs to be setup for long - * exposure mode. This will require us to use a long exposure scale - * factor provided by a shift operation in the sensor. - */ - while (frameLength > frameLengthMax) { - if (++shift > longExposureShiftMax) { - shift = longExposureShiftMax; - frameLength = frameLengthMax; - break; - } - frameLength >>= 1; - } - - if (shift) { - /* Account for any rounding in the scaled frame length value. */ - frameLength <<= shift; - exposureLines = CamHelperImx477::exposureLines(exposure, lineLength); - exposureLines = std::min(exposureLines, frameLength - frameIntegrationDiff); - exposure = CamHelperImx477::exposure(exposureLines, lineLength); - } - - return { frameLength - mode_.height, hblank }; -} - -void CamHelperImx477::getDelays(int &exposureDelay, int &gainDelay, - int &vblankDelay, int &hblankDelay) const -{ - exposureDelay = 2; - gainDelay = 2; - vblankDelay = 3; - hblankDelay = 3; -} - -bool CamHelperImx477::sensorEmbeddedDataPresent() const -{ - return true; -} - -void CamHelperImx477::populateMetadata(const MdParser::RegisterMap ®isters, - Metadata &metadata) const -{ - DeviceStatus deviceStatus; - - deviceStatus.lineLength = lineLengthPckToDuration(registers.at(lineLengthHiReg) * 256 + - registers.at(lineLengthLoReg)); - deviceStatus.shutterSpeed = exposure(registers.at(expHiReg) * 256 + registers.at(expLoReg), - deviceStatus.lineLength); - deviceStatus.analogueGain = gain(registers.at(gainHiReg) * 256 + registers.at(gainLoReg)); - deviceStatus.frameLength = registers.at(frameLengthHiReg) * 256 + registers.at(frameLengthLoReg); - deviceStatus.sensorTemperature = std::clamp(registers.at(temperatureReg), -20, 80); - - metadata.set("device.status", deviceStatus); -} - -static CamHelper *create() -{ - return new CamHelperImx477(); -} - -static RegisterCamHelper reg("imx477", &create); diff --git a/src/ipa/raspberrypi/cam_helper_imx519.cpp b/src/ipa/raspberrypi/cam_helper_imx519.cpp deleted file mode 100644 index c7262aa0..00000000 --- a/src/ipa/raspberrypi/cam_helper_imx519.cpp +++ /dev/null @@ -1,196 +0,0 @@ -/* SPDX-License-Identifier: BSD-2-Clause */ -/* - * Based on cam_helper_imx477.cpp - * Copyright (C) 2020, Raspberry Pi Ltd - * - * cam_helper_imx519.cpp - camera helper for imx519 sensor - * Copyright (C) 2021, Arducam Technology co., Ltd. - */ - -#include -#include -#include -#include -#include - -#include - -#include "cam_helper.h" -#include "md_parser.h" - -using namespace RPiController; -using namespace libcamera; -using libcamera::utils::Duration; - -namespace libcamera { -LOG_DECLARE_CATEGORY(IPARPI) -} - -/* - * We care about two gain registers and a pair of exposure registers. Their - * I2C addresses from the Sony IMX519 datasheet: - */ -constexpr uint32_t expHiReg = 0x0202; -constexpr uint32_t expLoReg = 0x0203; -constexpr uint32_t gainHiReg = 0x0204; -constexpr uint32_t gainLoReg = 0x0205; -constexpr uint32_t frameLengthHiReg = 0x0340; -constexpr uint32_t frameLengthLoReg = 0x0341; -constexpr uint32_t lineLengthHiReg = 0x0342; -constexpr uint32_t lineLengthLoReg = 0x0343; -constexpr std::initializer_list registerList = - { expHiReg, expLoReg, gainHiReg, gainLoReg, frameLengthHiReg, frameLengthLoReg, - lineLengthHiReg, lineLengthLoReg }; - -class CamHelperImx519 : public CamHelper -{ -public: - CamHelperImx519(); - uint32_t gainCode(double gain) const override; - double gain(uint32_t gainCode) const override; - void prepare(libcamera::Span buffer, Metadata &metadata) override; - std::pair getBlanking(Duration &exposure, Duration minFrameDuration, - Duration maxFrameDuration) const override; - void getDelays(int &exposureDelay, int &gainDelay, - int &vblankDelay, int &hblankDelay) const override; - bool sensorEmbeddedDataPresent() const override; - -private: - /* - * Smallest difference between the frame length and integration time, - * in units of lines. - */ - static constexpr int frameIntegrationDiff = 32; - /* Maximum frame length allowable for long exposure calculations. */ - static constexpr int frameLengthMax = 0xffdc; - /* Largest long exposure scale factor given as a left shift on the frame length. */ - static constexpr int longExposureShiftMax = 7; - - void populateMetadata(const MdParser::RegisterMap ®isters, - Metadata &metadata) const override; -}; - -CamHelperImx519::CamHelperImx519() - : CamHelper(std::make_unique(registerList), frameIntegrationDiff) -{ -} - -uint32_t CamHelperImx519::gainCode(double gain) const -{ - return static_cast(1024 - 1024 / gain); -} - -double CamHelperImx519::gain(uint32_t gainCode) const -{ - return 1024.0 / (1024 - gainCode); -} - -void CamHelperImx519::prepare(libcamera::Span buffer, Metadata &metadata) -{ - MdParser::RegisterMap registers; - DeviceStatus deviceStatus; - - if (metadata.get("device.status", deviceStatus)) { - LOG(IPARPI, Error) << "DeviceStatus not found from DelayedControls"; - return; - } - - parseEmbeddedData(buffer, metadata); - - /* - * The DeviceStatus struct is first populated with values obtained from - * DelayedControls. If this reports frame length is > frameLengthMax, - * it means we are using a long exposure mode. Since the long exposure - * scale factor is not returned back through embedded data, we must rely - * on the existing exposure lines and frame length values returned by - * DelayedControls. - * - * Otherwise, all values are updated with what is reported in the - * embedded data. - */ - if (deviceStatus.frameLength > frameLengthMax) { - DeviceStatus parsedDeviceStatus; - - metadata.get("device.status", parsedDeviceStatus); - parsedDeviceStatus.shutterSpeed = deviceStatus.shutterSpeed; - parsedDeviceStatus.frameLength = deviceStatus.frameLength; - metadata.set("device.status", parsedDeviceStatus); - - LOG(IPARPI, Debug) << "Metadata updated for long exposure: " - << parsedDeviceStatus; - } -} - -std::pair CamHelperImx519::getBlanking(Duration &exposure, - Duration minFrameDuration, - Duration maxFrameDuration) const -{ - uint32_t frameLength, exposureLines; - unsigned int shift = 0; - - auto [vblank, hblank] = CamHelper::getBlanking(exposure, minFrameDuration, - maxFrameDuration); - - frameLength = mode_.height + vblank; - Duration lineLength = hblankToLineLength(hblank); - - /* - * Check if the frame length calculated needs to be setup for long - * exposure mode. This will require us to use a long exposure scale - * factor provided by a shift operation in the sensor. - */ - while (frameLength > frameLengthMax) { - if (++shift > longExposureShiftMax) { - shift = longExposureShiftMax; - frameLength = frameLengthMax; - break; - } - frameLength >>= 1; - } - - if (shift) { - /* Account for any rounding in the scaled frame length value. */ - frameLength <<= shift; - exposureLines = CamHelperImx519::exposureLines(exposure, lineLength); - exposureLines = std::min(exposureLines, frameLength - frameIntegrationDiff); - exposure = CamHelperImx519::exposure(exposureLines, lineLength); - } - - return { frameLength - mode_.height, hblank }; -} - -void CamHelperImx519::getDelays(int &exposureDelay, int &gainDelay, - int &vblankDelay, int &hblankDelay) const -{ - exposureDelay = 2; - gainDelay = 2; - vblankDelay = 3; - hblankDelay = 3; -} - -bool CamHelperImx519::sensorEmbeddedDataPresent() const -{ - return true; -} - -void CamHelperImx519::populateMetadata(const MdParser::RegisterMap ®isters, - Metadata &metadata) const -{ - DeviceStatus deviceStatus; - - deviceStatus.lineLength = lineLengthPckToDuration(registers.at(lineLengthHiReg) * 256 + - registers.at(lineLengthLoReg)); - deviceStatus.shutterSpeed = exposure(registers.at(expHiReg) * 256 + registers.at(expLoReg), - deviceStatus.lineLength); - deviceStatus.analogueGain = gain(registers.at(gainHiReg) * 256 + registers.at(gainLoReg)); - deviceStatus.frameLength = registers.at(frameLengthHiReg) * 256 + registers.at(frameLengthLoReg); - - metadata.set("device.status", deviceStatus); -} - -static CamHelper *create() -{ - return new CamHelperImx519(); -} - -static RegisterCamHelper reg("imx519", &create); diff --git a/src/ipa/raspberrypi/cam_helper_imx708.cpp b/src/ipa/raspberrypi/cam_helper_imx708.cpp deleted file mode 100644 index 641ba18f..00000000 --- a/src/ipa/raspberrypi/cam_helper_imx708.cpp +++ /dev/null @@ -1,359 +0,0 @@ -/* SPDX-License-Identifier: BSD-2-Clause */ -/* - * Copyright (C) 2022, Raspberry Pi Ltd - * - * cam_helper_imx708.cpp - camera helper for imx708 sensor - */ - -#include -#include -#include -#include - -#include - -#include "controller/pdaf_data.h" - -#include "cam_helper.h" -#include "md_parser.h" - -using namespace RPiController; -using namespace libcamera; -using libcamera::utils::Duration; - -namespace libcamera { -LOG_DECLARE_CATEGORY(IPARPI) -} - -/* - * We care about two gain registers and a pair of exposure registers. Their - * I2C addresses from the Sony imx708 datasheet: - */ -constexpr uint32_t expHiReg = 0x0202; -constexpr uint32_t expLoReg = 0x0203; -constexpr uint32_t gainHiReg = 0x0204; -constexpr uint32_t gainLoReg = 0x0205; -constexpr uint32_t frameLengthHiReg = 0x0340; -constexpr uint32_t frameLengthLoReg = 0x0341; -constexpr uint32_t lineLengthHiReg = 0x0342; -constexpr uint32_t lineLengthLoReg = 0x0343; -constexpr uint32_t temperatureReg = 0x013a; -constexpr std::initializer_list registerList = - { expHiReg, expLoReg, gainHiReg, gainLoReg, lineLengthHiReg, - lineLengthLoReg, frameLengthHiReg, frameLengthLoReg, temperatureReg }; - -class CamHelperImx708 : public CamHelper -{ -public: - CamHelperImx708(); - uint32_t gainCode(double gain) const override; - double gain(uint32_t gain_code) const override; - void prepare(libcamera::Span buffer, Metadata &metadata) override; - void process(StatisticsPtr &stats, Metadata &metadata) override; - std::pair getBlanking(Duration &exposure, Duration minFrameDuration, - Duration maxFrameDuration) const override; - void getDelays(int &exposureDelay, int &gainDelay, - int &vblankDelay, int &hblankDelay) const override; - bool sensorEmbeddedDataPresent() const override; - double getModeSensitivity(const CameraMode &mode) const override; - unsigned int hideFramesModeSwitch() const override { return 1; } // seems to be required for HDR - -private: - /* - * Smallest difference between the frame length and integration time, - * in units of lines. - */ - static constexpr int frameIntegrationDiff = 22; - /* Maximum frame length allowable for long exposure calculations. */ - static constexpr int frameLengthMax = 0xffdc; - /* Largest long exposure scale factor given as a left shift on the frame length. */ - static constexpr int longExposureShiftMax = 7; - - static constexpr int pdafStatsRows = 12; - static constexpr int pdafStatsCols = 16; - - void populateMetadata(const MdParser::RegisterMap ®isters, - Metadata &metadata) const override; - - static bool parsePdafData(const uint8_t *ptr, size_t len, unsigned bpp, - PdafRegions &pdaf); - - bool parseAEHist(const uint8_t *ptr, size_t len, unsigned bpp); - void putAGCStatistics(StatisticsPtr stats); - - Histogram aeHistLinear_; - uint32_t aeHistAverage_; - bool aeHistValid_; -}; - -CamHelperImx708::CamHelperImx708() - : CamHelper(std::make_unique(registerList), frameIntegrationDiff), - aeHistLinear_{}, aeHistAverage_(0), aeHistValid_(false) -{ -} - -uint32_t CamHelperImx708::gainCode(double gain) const -{ - return static_cast(1024 - 1024 / gain); -} - -double CamHelperImx708::gain(uint32_t gain_code) const -{ - return 1024.0 / (1024 - gain_code); -} - -void CamHelperImx708::prepare(libcamera::Span buffer, Metadata &metadata) -{ - MdParser::RegisterMap registers; - DeviceStatus deviceStatus; - - LOG(IPARPI, Debug) << "Embedded buffer size: " << buffer.size(); - - if (metadata.get("device.status", deviceStatus)) { - LOG(IPARPI, Error) << "DeviceStatus not found from DelayedControls"; - return; - } - - parseEmbeddedData(buffer, metadata); - - /* - * Parse PDAF data, which we expect to occupy the third scanline - * of embedded data. As PDAF is quite sensor-specific, it's parsed here. - */ - size_t bytesPerLine = (mode_.width * mode_.bitdepth) >> 3; - - if (buffer.size() > 2 * bytesPerLine) { - PdafRegions pdaf; - if (parsePdafData(&buffer[2 * bytesPerLine], - buffer.size() - 2 * bytesPerLine, - mode_.bitdepth, pdaf)) - metadata.set("pdaf.regions", pdaf); - } - - /* Parse AE-HIST data where present */ - if (buffer.size() > 3 * bytesPerLine) { - aeHistValid_ = parseAEHist(&buffer[3 * bytesPerLine], - buffer.size() - 3 * bytesPerLine, - mode_.bitdepth); - } - - /* - * The DeviceStatus struct is first populated with values obtained from - * DelayedControls. If this reports frame length is > frameLengthMax, - * it means we are using a long exposure mode. Since the long exposure - * scale factor is not returned back through embedded data, we must rely - * on the existing exposure lines and frame length values returned by - * DelayedControls. - * - * Otherwise, all values are updated with what is reported in the - * embedded data. - */ - if (deviceStatus.frameLength > frameLengthMax) { - DeviceStatus parsedDeviceStatus; - - metadata.get("device.status", parsedDeviceStatus); - parsedDeviceStatus.shutterSpeed = deviceStatus.shutterSpeed; - parsedDeviceStatus.frameLength = deviceStatus.frameLength; - metadata.set("device.status", parsedDeviceStatus); - - LOG(IPARPI, Debug) << "Metadata updated for long exposure: " - << parsedDeviceStatus; - } -} - -void CamHelperImx708::process(StatisticsPtr &stats, [[maybe_unused]] Metadata &metadata) -{ - if (aeHistValid_) - putAGCStatistics(stats); -} - -std::pair CamHelperImx708::getBlanking(Duration &exposure, - Duration minFrameDuration, - Duration maxFrameDuration) const -{ - uint32_t frameLength, exposureLines; - unsigned int shift = 0; - - auto [vblank, hblank] = CamHelper::getBlanking(exposure, minFrameDuration, - maxFrameDuration); - - frameLength = mode_.height + vblank; - Duration lineLength = hblankToLineLength(hblank); - - /* - * Check if the frame length calculated needs to be setup for long - * exposure mode. This will require us to use a long exposure scale - * factor provided by a shift operation in the sensor. - */ - while (frameLength > frameLengthMax) { - if (++shift > longExposureShiftMax) { - shift = longExposureShiftMax; - frameLength = frameLengthMax; - break; - } - frameLength >>= 1; - } - - if (shift) { - /* Account for any rounding in the scaled frame length value. */ - frameLength <<= shift; - exposureLines = CamHelper::exposureLines(exposure, lineLength); - exposureLines = std::min(exposureLines, frameLength - frameIntegrationDiff); - exposure = CamHelper::exposure(exposureLines, lineLength); - } - - return { frameLength - mode_.height, hblank }; -} - -void CamHelperImx708::getDelays(int &exposureDelay, int &gainDelay, - int &vblankDelay, int &hblankDelay) const -{ - exposureDelay = 2; - gainDelay = 2; - vblankDelay = 3; - hblankDelay = 3; -} - -bool CamHelperImx708::sensorEmbeddedDataPresent() const -{ - return true; -} - -double CamHelperImx708::getModeSensitivity(const CameraMode &mode) const -{ - /* In binned modes, sensitivity increases by a factor of 2 */ - return (mode.width > 2304) ? 1.0 : 2.0; -} - -void CamHelperImx708::populateMetadata(const MdParser::RegisterMap ®isters, - Metadata &metadata) const -{ - DeviceStatus deviceStatus; - - deviceStatus.lineLength = lineLengthPckToDuration(registers.at(lineLengthHiReg) * 256 + - registers.at(lineLengthLoReg)); - deviceStatus.shutterSpeed = exposure(registers.at(expHiReg) * 256 + registers.at(expLoReg), - deviceStatus.lineLength); - deviceStatus.analogueGain = gain(registers.at(gainHiReg) * 256 + registers.at(gainLoReg)); - deviceStatus.frameLength = registers.at(frameLengthHiReg) * 256 + registers.at(frameLengthLoReg); - deviceStatus.sensorTemperature = std::clamp(registers.at(temperatureReg), -20, 80); - - metadata.set("device.status", deviceStatus); -} - -bool CamHelperImx708::parsePdafData(const uint8_t *ptr, size_t len, - unsigned bpp, PdafRegions &pdaf) -{ - size_t step = bpp >> 1; /* bytes per PDAF grid entry */ - - if (bpp < 10 || bpp > 12 || len < 194 * step || ptr[0] != 0 || ptr[1] >= 0x40) { - LOG(IPARPI, Error) << "PDAF data in unsupported format"; - return false; - } - - pdaf.init({ pdafStatsCols, pdafStatsRows }); - - ptr += 2 * step; - for (unsigned i = 0; i < pdafStatsRows; ++i) { - for (unsigned j = 0; j < pdafStatsCols; ++j) { - unsigned c = (ptr[0] << 3) | (ptr[1] >> 5); - int p = (((ptr[1] & 0x0F) - (ptr[1] & 0x10)) << 6) | (ptr[2] >> 2); - PdafData pdafData; - pdafData.conf = c; - pdafData.phase = c ? p : 0; - pdaf.set(libcamera::Point(j, i), { pdafData, 1, 0 }); - ptr += step; - } - } - - return true; -} - -bool CamHelperImx708::parseAEHist(const uint8_t *ptr, size_t len, unsigned bpp) -{ - static constexpr unsigned int PipelineBits = Statistics::NormalisationFactorPow2; - - uint64_t count = 0, sum = 0; - size_t step = bpp >> 1; /* bytes per histogram bin */ - uint32_t hist[128]; - - if (len < 144 * step) - return false; - - /* - * Read the 128 bin linear histogram, which by default covers - * the full range of the HDR shortest exposure (small values are - * expected to dominate, so pixel-value resolution will be poor). - */ - for (unsigned i = 0; i < 128; ++i) { - if (ptr[3] != 0x55) - return false; - uint32_t c = (ptr[0] << 14) + (ptr[1] << 6) + (ptr[2] >> 2); - hist[i] = c >> 2; /* pixels to quads */ - if (i != 0) { - count += c; - sum += c * - (i * (1u << (PipelineBits - 7)) + - (1u << (PipelineBits - 8))); - } - ptr += step; - } - - /* - * Now use the first 9 bins of the log histogram (these should be - * subdivisions of the smallest linear bin), to get a more accurate - * average value. Don't assume that AEHIST1_AVERAGE is present. - */ - for (unsigned i = 0; i < 9; ++i) { - if (ptr[3] != 0x55) - return false; - uint32_t c = (ptr[0] << 14) + (ptr[1] << 6) + (ptr[2] >> 2); - count += c; - sum += c * - ((3u << PipelineBits) >> (17 - i)); - ptr += step; - } - if ((unsigned)((ptr[0] << 12) + (ptr[1] << 4) + (ptr[2] >> 4)) != - hist[1]) { - LOG(IPARPI, Error) << "Lin/Log histogram mismatch"; - return false; - } - - aeHistLinear_ = Histogram(hist, 128); - aeHistAverage_ = count ? (sum / count) : 0; - - return count != 0; -} - -void CamHelperImx708::putAGCStatistics(StatisticsPtr stats) -{ - /* - * For HDR mode, copy sensor's AE/AGC statistics over ISP's, so the - * AGC algorithm sees a linear response to exposure and gain changes. - * - * Histogram: Just copy the "raw" histogram over the tone-mapped one, - * although they have different distributions (raw values are lower). - * Tuning should either ignore it, or constrain for highlights only. - * - * Average: Overwrite all regional averages with a global raw average, - * scaled by a fiddle-factor so that a conventional (non-HDR) y_target - * of e.g. 0.17 will map to a suitable level for HDR. - */ - stats->yHist = aeHistLinear_; - - constexpr unsigned int HdrHeadroomFactor = 4; - uint64_t v = HdrHeadroomFactor * aeHistAverage_; - for (auto ®ion : stats->agcRegions) { - region.val.rSum = region.val.gSum = region.val.bSum = region.counted * v; - } -} - -static CamHelper *create() -{ - return new CamHelperImx708(); -} - -static RegisterCamHelper reg("imx708", &create); -static RegisterCamHelper regWide("imx708_wide", &create); -static RegisterCamHelper regNoIr("imx708_noir", &create); -static RegisterCamHelper regWideNoIr("imx708_wide_noir", &create); diff --git a/src/ipa/raspberrypi/cam_helper_ov5647.cpp b/src/ipa/raspberrypi/cam_helper_ov5647.cpp deleted file mode 100644 index 5a99083d..00000000 --- a/src/ipa/raspberrypi/cam_helper_ov5647.cpp +++ /dev/null @@ -1,109 +0,0 @@ -/* SPDX-License-Identifier: BSD-2-Clause */ -/* - * Copyright (C) 2019, Raspberry Pi Ltd - * - * cam_helper_ov5647.cpp - camera information for ov5647 sensor - */ - -#include - -#include "cam_helper.h" - -using namespace RPiController; - -class CamHelperOv5647 : public CamHelper -{ -public: - CamHelperOv5647(); - uint32_t gainCode(double gain) const override; - double gain(uint32_t gainCode) const override; - void getDelays(int &exposureDelay, int &gainDelay, - int &vblankDelay, int &hblankDelay) const override; - unsigned int hideFramesStartup() const override; - unsigned int hideFramesModeSwitch() const override; - unsigned int mistrustFramesStartup() const override; - unsigned int mistrustFramesModeSwitch() const override; - -private: - /* - * Smallest difference between the frame length and integration time, - * in units of lines. - */ - static constexpr int frameIntegrationDiff = 4; -}; - -/* - * OV5647 doesn't output metadata, so we have to use the "unicam parser" which - * works by counting frames. - */ - -CamHelperOv5647::CamHelperOv5647() - : CamHelper({}, frameIntegrationDiff) -{ -} - -uint32_t CamHelperOv5647::gainCode(double gain) const -{ - return static_cast(gain * 16.0); -} - -double CamHelperOv5647::gain(uint32_t gainCode) const -{ - return static_cast(gainCode) / 16.0; -} - -void CamHelperOv5647::getDelays(int &exposureDelay, int &gainDelay, - int &vblankDelay, int &hblankDelay) const -{ - /* - * We run this sensor in a mode where the gain delay is bumped up to - * 2. It seems to be the only way to make the delays "predictable". - */ - exposureDelay = 2; - gainDelay = 2; - vblankDelay = 2; - hblankDelay = 2; -} - -unsigned int CamHelperOv5647::hideFramesStartup() const -{ - /* - * On startup, we get a couple of under-exposed frames which - * we don't want shown. - */ - return 2; -} - -unsigned int CamHelperOv5647::hideFramesModeSwitch() const -{ - /* - * After a mode switch, we get a couple of under-exposed frames which - * we don't want shown. - */ - return 2; -} - -unsigned int CamHelperOv5647::mistrustFramesStartup() const -{ - /* - * First couple of frames are under-exposed and are no good for control - * algos. - */ - return 2; -} - -unsigned int CamHelperOv5647::mistrustFramesModeSwitch() const -{ - /* - * First couple of frames are under-exposed even after a simple - * mode switch, and are no good for control algos. - */ - return 2; -} - -static CamHelper *create() -{ - return new CamHelperOv5647(); -} - -static RegisterCamHelper reg("ov5647", &create); diff --git a/src/ipa/raspberrypi/cam_helper_ov9281.cpp b/src/ipa/raspberrypi/cam_helper_ov9281.cpp deleted file mode 100644 index 86c5bc4c..00000000 --- a/src/ipa/raspberrypi/cam_helper_ov9281.cpp +++ /dev/null @@ -1,66 +0,0 @@ -/* SPDX-License-Identifier: BSD-2-Clause */ -/* - * Copyright (C) 2021, Raspberry Pi Ltd - * - * cam_helper_ov9281.cpp - camera information for ov9281 sensor - */ - -#include - -#include "cam_helper.h" - -using namespace RPiController; - -class CamHelperOv9281 : public CamHelper -{ -public: - CamHelperOv9281(); - uint32_t gainCode(double gain) const override; - double gain(uint32_t gainCode) const override; - void getDelays(int &exposureDelay, int &gainDelay, - int &vblankDelay, int &hblankDelay) const override; - -private: - /* - * Smallest difference between the frame length and integration time, - * in units of lines. - */ - static constexpr int frameIntegrationDiff = 4; -}; - -/* - * OV9281 doesn't output metadata, so we have to use the "unicam parser" which - * works by counting frames. - */ - -CamHelperOv9281::CamHelperOv9281() - : CamHelper({}, frameIntegrationDiff) -{ -} - -uint32_t CamHelperOv9281::gainCode(double gain) const -{ - return static_cast(gain * 16.0); -} - -double CamHelperOv9281::gain(uint32_t gainCode) const -{ - return static_cast(gainCode) / 16.0; -} - -void CamHelperOv9281::getDelays(int &exposureDelay, int &gainDelay, - int &vblankDelay, int &hblankDelay) const -{ - /* The driver appears to behave as follows: */ - exposureDelay = 2; - gainDelay = 2; - vblankDelay = 2; - hblankDelay = 2; -} - -static CamHelper *create() -{ - return new CamHelperOv9281(); -} - -static RegisterCamHelper reg("ov9281", &create); diff --git a/src/ipa/raspberrypi/controller/af_algorithm.h b/src/ipa/raspberrypi/controller/af_algorithm.h deleted file mode 100644 index ad9b5754..00000000 --- a/src/ipa/raspberrypi/controller/af_algorithm.h +++ /dev/null @@ -1,76 +0,0 @@ -/* SPDX-License-Identifier: BSD-2-Clause */ -/* - * Copyright (C) 2022, Raspberry Pi Ltd - * - * af_algorithm.hpp - auto focus algorithm interface - */ -#pragma once - -#include - -#include - -#include "algorithm.h" - -namespace RPiController { - -class AfAlgorithm : public Algorithm -{ -public: - AfAlgorithm(Controller *controller) - : Algorithm(controller) {} - - /* - * An autofocus algorithm should provide the following calls. - * - * Where a ControlList combines a change of AfMode with other AF - * controls, setMode() should be called first, to ensure the - * algorithm will be in the correct state to handle controls. - * - * setLensPosition() returns true if the mode was AfModeManual and - * the lens position has changed, otherwise returns false. When it - * returns true, hwpos should be sent immediately to the lens driver. - * - * getMode() is provided mainly for validating controls. - * getLensPosition() is provided for populating DeviceStatus. - */ - - enum AfRange { AfRangeNormal = 0, - AfRangeMacro, - AfRangeFull, - AfRangeMax }; - - enum AfSpeed { AfSpeedNormal = 0, - AfSpeedFast, - AfSpeedMax }; - - enum AfMode { AfModeManual = 0, - AfModeAuto, - AfModeContinuous }; - - enum AfPause { AfPauseImmediate = 0, - AfPauseDeferred, - AfPauseResume }; - - virtual void setRange([[maybe_unused]] AfRange range) - { - } - virtual void setSpeed([[maybe_unused]] AfSpeed speed) - { - } - virtual void setMetering([[maybe_unused]] bool use_windows) - { - } - virtual void setWindows([[maybe_unused]] libcamera::Span const &wins) - { - } - virtual void setMode(AfMode mode) = 0; - virtual AfMode getMode() const = 0; - virtual bool setLensPosition(double dioptres, int32_t *hwpos) = 0; - virtual std::optional getLensPosition() const = 0; - virtual void triggerScan() = 0; - virtual void cancelScan() = 0; - virtual void pause(AfPause pause) = 0; -}; - -} // namespace RPiController diff --git a/src/ipa/raspberrypi/controller/af_status.h b/src/ipa/raspberrypi/controller/af_status.h deleted file mode 100644 index 92c08812..00000000 --- a/src/ipa/raspberrypi/controller/af_status.h +++ /dev/null @@ -1,35 +0,0 @@ -/* SPDX-License-Identifier: BSD-2-Clause */ -/* - * Copyright (C) 2022, Raspberry Pi Ltd - * - * af_status.h - AF control algorithm status - */ -#pragma once - -#include - -/* - * The AF algorithm should post the following structure into the image's - * "af.status" metadata. lensSetting should control the lens. - */ - -enum class AfState { - Idle = 0, - Scanning, - Focused, - Failed -}; - -enum class AfPauseState { - Running = 0, - Pausing, - Paused -}; - -struct AfStatus { - /* state for reporting */ - AfState state; - AfPauseState pauseState; - /* lensSetting should be sent to the lens driver, when valid */ - std::optional lensSetting; -}; diff --git a/src/ipa/raspberrypi/controller/agc_algorithm.h b/src/ipa/raspberrypi/controller/agc_algorithm.h deleted file mode 100644 index 36e6c110..00000000 --- a/src/ipa/raspberrypi/controller/agc_algorithm.h +++ /dev/null @@ -1,33 +0,0 @@ -/* SPDX-License-Identifier: BSD-2-Clause */ -/* - * Copyright (C) 2019, Raspberry Pi Ltd - * - * agc_algorithm.h - AGC/AEC control algorithm interface - */ -#pragma once - -#include - -#include "algorithm.h" - -namespace RPiController { - -class AgcAlgorithm : public Algorithm -{ -public: - AgcAlgorithm(Controller *controller) : Algorithm(controller) {} - /* An AGC algorithm must provide the following: */ - virtual unsigned int getConvergenceFrames() const = 0; - virtual void setEv(double ev) = 0; - virtual void setFlickerPeriod(libcamera::utils::Duration flickerPeriod) = 0; - virtual void setFixedShutter(libcamera::utils::Duration fixedShutter) = 0; - virtual void setMaxShutter(libcamera::utils::Duration maxShutter) = 0; - virtual void setFixedAnalogueGain(double fixedAnalogueGain) = 0; - virtual void setMeteringMode(std::string const &meteringModeName) = 0; - virtual void setExposureMode(std::string const &exposureModeName) = 0; - virtual void setConstraintMode(std::string const &contraintModeName) = 0; - virtual void enableAuto() = 0; - virtual void disableAuto() = 0; -}; - -} /* namespace RPiController */ diff --git a/src/ipa/raspberrypi/controller/agc_status.h b/src/ipa/raspberrypi/controller/agc_status.h deleted file mode 100644 index 6abf09d9..00000000 --- a/src/ipa/raspberrypi/controller/agc_status.h +++ /dev/null @@ -1,37 +0,0 @@ -/* SPDX-License-Identifier: BSD-2-Clause */ -/* - * Copyright (C) 2019, Raspberry Pi Ltd - * - * agc_status.h - AGC/AEC control algorithm status - */ -#pragma once - -#include - -/* - * The AGC algorithm should post the following structure into the image's - * "agc.status" metadata. - */ - -/* - * Note: total_exposure_value will be reported as zero until the algorithm has - * seen statistics and calculated meaningful values. The contents should be - * ignored until then. - */ - -struct AgcStatus { - libcamera::utils::Duration totalExposureValue; /* value for all exposure and gain for this image */ - libcamera::utils::Duration targetExposureValue; /* (unfiltered) target total exposure AGC is aiming for */ - libcamera::utils::Duration shutterTime; - double analogueGain; - char exposureMode[32]; - char constraintMode[32]; - char meteringMode[32]; - double ev; - libcamera::utils::Duration flickerPeriod; - int floatingRegionEnable; - libcamera::utils::Duration fixedShutter; - double fixedAnalogueGain; - double digitalGain; - int locked; -}; diff --git a/src/ipa/raspberrypi/controller/algorithm.cpp b/src/ipa/raspberrypi/controller/algorithm.cpp deleted file mode 100644 index a957fde5..00000000 --- a/src/ipa/raspberrypi/controller/algorithm.cpp +++ /dev/null @@ -1,56 +0,0 @@ -/* SPDX-License-Identifier: BSD-2-Clause */ -/* - * Copyright (C) 2019, Raspberry Pi Ltd - * - * algorithm.cpp - ISP control algorithms - */ - -#include "algorithm.h" - -using namespace RPiController; - -int Algorithm::read([[maybe_unused]] const libcamera::YamlObject ¶ms) -{ - return 0; -} - -void Algorithm::initialise() -{ -} - -void Algorithm::switchMode([[maybe_unused]] CameraMode const &cameraMode, - [[maybe_unused]] Metadata *metadata) -{ -} - -void Algorithm::prepare([[maybe_unused]] Metadata *imageMetadata) -{ -} - -void Algorithm::process([[maybe_unused]] StatisticsPtr &stats, - [[maybe_unused]] Metadata *imageMetadata) -{ -} - -/* For registering algorithms with the system: */ - -namespace { - -std::map &algorithms() -{ - static std::map algorithms; - return algorithms; -} - -} /* namespace */ - -std::map const &RPiController::getAlgorithms() -{ - return algorithms(); -} - -RegisterAlgorithm::RegisterAlgorithm(char const *name, - AlgoCreateFunc createFunc) -{ - algorithms()[std::string(name)] = createFunc; -} diff --git a/src/ipa/raspberrypi/controller/algorithm.h b/src/ipa/raspberrypi/controller/algorithm.h deleted file mode 100644 index 4aa814eb..00000000 --- a/src/ipa/raspberrypi/controller/algorithm.h +++ /dev/null @@ -1,68 +0,0 @@ -/* SPDX-License-Identifier: BSD-2-Clause */ -/* - * Copyright (C) 2019, Raspberry Pi Ltd - * - * algorithm.h - ISP control algorithm interface - */ -#pragma once - -/* - * All algorithms should be derived from this class and made available to the - * Controller. - */ - -#include -#include -#include - -#include "libcamera/internal/yaml_parser.h" - -#include "controller.h" - -namespace RPiController { - -/* This defines the basic interface for all control algorithms. */ - -class Algorithm -{ -public: - Algorithm(Controller *controller) - : controller_(controller) - { - } - virtual ~Algorithm() = default; - virtual char const *name() const = 0; - virtual int read(const libcamera::YamlObject ¶ms); - virtual void initialise(); - virtual void switchMode(CameraMode const &cameraMode, Metadata *metadata); - virtual void prepare(Metadata *imageMetadata); - virtual void process(StatisticsPtr &stats, Metadata *imageMetadata); - Metadata &getGlobalMetadata() const - { - return controller_->getGlobalMetadata(); - } - const std::string &getTarget() const - { - return controller_->getTarget(); - } - const Controller::HardwareConfig &getHardwareConfig() const - { - return controller_->getHardwareConfig(); - } - -private: - Controller *controller_; -}; - -/* - * This code is for automatic registration of Front End algorithms with the - * system. - */ - -typedef Algorithm *(*AlgoCreateFunc)(Controller *controller); -struct RegisterAlgorithm { - RegisterAlgorithm(char const *name, AlgoCreateFunc createFunc); -}; -std::map const &getAlgorithms(); - -} /* namespace RPiController */ diff --git a/src/ipa/raspberrypi/controller/alsc_status.h b/src/ipa/raspberrypi/controller/alsc_status.h deleted file mode 100644 index 49a9f4a0..00000000 --- a/src/ipa/raspberrypi/controller/alsc_status.h +++ /dev/null @@ -1,22 +0,0 @@ -/* SPDX-License-Identifier: BSD-2-Clause */ -/* - * Copyright (C) 2019, Raspberry Pi Ltd - * - * alsc_status.h - ALSC (auto lens shading correction) control algorithm status - */ -#pragma once - -#include - -/* - * The ALSC algorithm should post the following structure into the image's - * "alsc.status" metadata. - */ - -struct AlscStatus { - std::vector r; - std::vector g; - std::vector b; - unsigned int rows; - unsigned int cols; -}; diff --git a/src/ipa/raspberrypi/controller/awb_algorithm.h b/src/ipa/raspberrypi/controller/awb_algorithm.h deleted file mode 100644 index 8462c4db..00000000 --- a/src/ipa/raspberrypi/controller/awb_algorithm.h +++ /dev/null @@ -1,25 +0,0 @@ -/* SPDX-License-Identifier: BSD-2-Clause */ -/* - * Copyright (C) 2019, Raspberry Pi Ltd - * - * awb_algorithm.h - AWB control algorithm interface - */ -#pragma once - -#include "algorithm.h" - -namespace RPiController { - -class AwbAlgorithm : public Algorithm -{ -public: - AwbAlgorithm(Controller *controller) : Algorithm(controller) {} - /* An AWB algorithm must provide the following: */ - virtual unsigned int getConvergenceFrames() const = 0; - virtual void setMode(std::string const &modeName) = 0; - virtual void setManualGains(double manualR, double manualB) = 0; - virtual void enableAuto() = 0; - virtual void disableAuto() = 0; -}; - -} /* namespace RPiController */ diff --git a/src/ipa/raspberrypi/controller/awb_status.h b/src/ipa/raspberrypi/controller/awb_status.h deleted file mode 100644 index dd5a79e3..00000000 --- a/src/ipa/raspberrypi/controller/awb_status.h +++ /dev/null @@ -1,20 +0,0 @@ -/* SPDX-License-Identifier: BSD-2-Clause */ -/* - * Copyright (C) 2019, Raspberry Pi Ltd - * - * awb_status.h - AWB control algorithm status - */ -#pragma once - -/* - * The AWB algorithm places its results into both the image and global metadata, - * under the tag "awb.status". - */ - -struct AwbStatus { - char mode[32]; - double temperatureK; - double gainR; - double gainG; - double gainB; -}; diff --git a/src/ipa/raspberrypi/controller/black_level_status.h b/src/ipa/raspberrypi/controller/black_level_status.h deleted file mode 100644 index fd5e4ccb..00000000 --- a/src/ipa/raspberrypi/controller/black_level_status.h +++ /dev/null @@ -1,15 +0,0 @@ -/* SPDX-License-Identifier: BSD-2-Clause */ -/* - * Copyright (C) 2019, Raspberry Pi Ltd - * - * black_level_status.h - black level control algorithm status - */ -#pragma once - -/* The "black level" algorithm stores the black levels to use. */ - -struct BlackLevelStatus { - uint16_t blackLevelR; /* out of 16 bits */ - uint16_t blackLevelG; - uint16_t blackLevelB; -}; diff --git a/src/ipa/raspberrypi/controller/camera_mode.h b/src/ipa/raspberrypi/controller/camera_mode.h deleted file mode 100644 index 63b11778..00000000 --- a/src/ipa/raspberrypi/controller/camera_mode.h +++ /dev/null @@ -1,59 +0,0 @@ -/* SPDX-License-Identifier: BSD-2-Clause */ -/* - * Copyright (C) 2019-2020, Raspberry Pi Ltd - * - * camera_mode.h - description of a particular operating mode of a sensor - */ -#pragma once - -#include - -#include - -/* - * Description of a "camera mode", holding enough information for control - * algorithms to adapt their behaviour to the different modes of the camera, - * including binning, scaling, cropping etc. - */ - -struct CameraMode { - /* bit depth of the raw camera output */ - uint32_t bitdepth; - /* size in pixels of frames in this mode */ - uint16_t width; - uint16_t height; - /* size of full resolution uncropped frame ("sensor frame") */ - uint16_t sensorWidth; - uint16_t sensorHeight; - /* binning factor (1 = no binning, 2 = 2-pixel binning etc.) */ - uint8_t binX; - uint8_t binY; - /* location of top left pixel in the sensor frame */ - uint16_t cropX; - uint16_t cropY; - /* scaling factor (so if uncropped, width*scaleX is sensorWidth) */ - double scaleX; - double scaleY; - /* scaling of the noise compared to the native sensor mode */ - double noiseFactor; - /* minimum and maximum line time and frame durations */ - libcamera::utils::Duration minLineLength; - libcamera::utils::Duration maxLineLength; - libcamera::utils::Duration minFrameDuration; - libcamera::utils::Duration maxFrameDuration; - /* any camera transform *not* reflected already in the camera tuning */ - libcamera::Transform transform; - /* minimum and maximum frame lengths in units of lines */ - uint32_t minFrameLength; - uint32_t maxFrameLength; - /* sensitivity of this mode */ - double sensitivity; - /* pixel clock rate */ - uint64_t pixelRate; - /* Mode specific shutter speed limits */ - libcamera::utils::Duration minShutter; - libcamera::utils::Duration maxShutter; - /* Mode specific analogue gain limits */ - double minAnalogueGain; - double maxAnalogueGain; -}; diff --git a/src/ipa/raspberrypi/controller/ccm_algorithm.h b/src/ipa/raspberrypi/controller/ccm_algorithm.h deleted file mode 100644 index e2c4d771..00000000 --- a/src/ipa/raspberrypi/controller/ccm_algorithm.h +++ /dev/null @@ -1,21 +0,0 @@ -/* SPDX-License-Identifier: BSD-2-Clause */ -/* - * Copyright (C) 2019, Raspberry Pi Ltd - * - * ccm_algorithm.h - CCM (colour correction matrix) control algorithm interface - */ -#pragma once - -#include "algorithm.h" - -namespace RPiController { - -class CcmAlgorithm : public Algorithm -{ -public: - CcmAlgorithm(Controller *controller) : Algorithm(controller) {} - /* A CCM algorithm must provide the following: */ - virtual void setSaturation(double saturation) = 0; -}; - -} /* namespace RPiController */ diff --git a/src/ipa/raspberrypi/controller/ccm_status.h b/src/ipa/raspberrypi/controller/ccm_status.h deleted file mode 100644 index 5e28ee7c..00000000 --- a/src/ipa/raspberrypi/controller/ccm_status.h +++ /dev/null @@ -1,14 +0,0 @@ -/* SPDX-License-Identifier: BSD-2-Clause */ -/* - * Copyright (C) 2019, Raspberry Pi Ltd - * - * ccm_status.h - CCM (colour correction matrix) control algorithm status - */ -#pragma once - -/* The "ccm" algorithm generates an appropriate colour matrix. */ - -struct CcmStatus { - double matrix[9]; - double saturation; -}; diff --git a/src/ipa/raspberrypi/controller/contrast_algorithm.h b/src/ipa/raspberrypi/controller/contrast_algorithm.h deleted file mode 100644 index ce17a4f9..00000000 --- a/src/ipa/raspberrypi/controller/contrast_algorithm.h +++ /dev/null @@ -1,22 +0,0 @@ -/* SPDX-License-Identifier: BSD-2-Clause */ -/* - * Copyright (C) 2019, Raspberry Pi Ltd - * - * contrast_algorithm.h - contrast (gamma) control algorithm interface - */ -#pragma once - -#include "algorithm.h" - -namespace RPiController { - -class ContrastAlgorithm : public Algorithm -{ -public: - ContrastAlgorithm(Controller *controller) : Algorithm(controller) {} - /* A contrast algorithm must provide the following: */ - virtual void setBrightness(double brightness) = 0; - virtual void setContrast(double contrast) = 0; -}; - -} /* namespace RPiController */ diff --git a/src/ipa/raspberrypi/controller/contrast_status.h b/src/ipa/raspberrypi/controller/contrast_status.h deleted file mode 100644 index fb9fe4ba..00000000 --- a/src/ipa/raspberrypi/controller/contrast_status.h +++ /dev/null @@ -1,20 +0,0 @@ -/* SPDX-License-Identifier: BSD-2-Clause */ -/* - * Copyright (C) 2019, Raspberry Pi Ltd - * - * contrast_status.h - contrast (gamma) control algorithm status - */ -#pragma once - -#include "pwl.h" - -/* - * The "contrast" algorithm creates a gamma curve, optionally doing a little bit - * of contrast stretching based on the AGC histogram. - */ - -struct ContrastStatus { - RPiController::Pwl gammaCurve; - double brightness; - double contrast; -}; diff --git a/src/ipa/raspberrypi/controller/controller.cpp b/src/ipa/raspberrypi/controller/controller.cpp deleted file mode 100644 index fa172113..00000000 --- a/src/ipa/raspberrypi/controller/controller.cpp +++ /dev/null @@ -1,181 +0,0 @@ -/* SPDX-License-Identifier: BSD-2-Clause */ -/* - * Copyright (C) 2019, Raspberry Pi Ltd - * - * controller.cpp - ISP controller - */ - -#include - -#include -#include - -#include "libcamera/internal/yaml_parser.h" - -#include "algorithm.h" -#include "controller.h" - -using namespace RPiController; -using namespace libcamera; - -LOG_DEFINE_CATEGORY(RPiController) - -static const std::map HardwareConfigMap = { - { - "bcm2835", - { - /* - * There are only ever 15 AGC regions computed by the firmware - * due to zoning, but the HW defines AGC_REGIONS == 16! - */ - .agcRegions = { 15 , 1 }, - .agcZoneWeights = { 15 , 1 }, - .awbRegions = { 16, 12 }, - .focusRegions = { 4, 3 }, - .numHistogramBins = 128, - .numGammaPoints = 33, - .pipelineWidth = 13 - } - }, -}; - -Controller::Controller() - : switchModeCalled_(false) -{ -} - -Controller::~Controller() {} - -int Controller::read(char const *filename) -{ - File file(filename); - if (!file.open(File::OpenModeFlag::ReadOnly)) { - LOG(RPiController, Warning) - << "Failed to open tuning file '" << filename << "'"; - return -EINVAL; - } - - std::unique_ptr root = YamlParser::parse(file); - double version = (*root)["version"].get(1.0); - target_ = (*root)["target"].get("bcm2835"); - - if (version < 2.0) { - LOG(RPiController, Warning) - << "This format of the tuning file will be deprecated soon!" - << " Please use the convert_tuning.py utility to update to version 2.0."; - - for (auto const &[key, value] : root->asDict()) { - int ret = createAlgorithm(key, value); - if (ret) - return ret; - } - } else if (version < 3.0) { - if (!root->contains("algorithms")) { - LOG(RPiController, Error) - << "Tuning file " << filename - << " does not have an \"algorithms\" list!"; - return -EINVAL; - } - - for (auto const &rootAlgo : (*root)["algorithms"].asList()) - for (auto const &[key, value] : rootAlgo.asDict()) { - int ret = createAlgorithm(key, value); - if (ret) - return ret; - } - } else { - LOG(RPiController, Error) - << "Unrecognised version " << version - << " for the tuning file " << filename; - return -EINVAL; - } - - return 0; -} - -int Controller::createAlgorithm(const std::string &name, const YamlObject ¶ms) -{ - auto it = getAlgorithms().find(name); - if (it == getAlgorithms().end()) { - LOG(RPiController, Warning) - << "No algorithm found for \"" << name << "\""; - return 0; - } - - Algorithm *algo = (*it->second)(this); - int ret = algo->read(params); - if (ret) - return ret; - - algorithms_.push_back(AlgorithmPtr(algo)); - return 0; -} - -void Controller::initialise() -{ - for (auto &algo : algorithms_) - algo->initialise(); -} - -void Controller::switchMode(CameraMode const &cameraMode, Metadata *metadata) -{ - for (auto &algo : algorithms_) - algo->switchMode(cameraMode, metadata); - switchModeCalled_ = true; -} - -void Controller::prepare(Metadata *imageMetadata) -{ - assert(switchModeCalled_); - for (auto &algo : algorithms_) - algo->prepare(imageMetadata); -} - -void Controller::process(StatisticsPtr stats, Metadata *imageMetadata) -{ - assert(switchModeCalled_); - for (auto &algo : algorithms_) - algo->process(stats, imageMetadata); -} - -Metadata &Controller::getGlobalMetadata() -{ - return globalMetadata_; -} - -Algorithm *Controller::getAlgorithm(std::string const &name) const -{ - /* - * The passed name must be the entire algorithm name, or must match the - * last part of it with a period (.) just before. - */ - size_t nameLen = name.length(); - for (auto &algo : algorithms_) { - char const *algoName = algo->name(); - size_t algoNameLen = strlen(algoName); - if (algoNameLen >= nameLen && - strcasecmp(name.c_str(), - algoName + algoNameLen - nameLen) == 0 && - (nameLen == algoNameLen || - algoName[algoNameLen - nameLen - 1] == '.')) - return algo.get(); - } - return nullptr; -} - -const std::string &Controller::getTarget() const -{ - return target_; -} - -const Controller::HardwareConfig &Controller::getHardwareConfig() const -{ - auto cfg = HardwareConfigMap.find(getTarget()); - - /* - * This really should not happen, the IPA ought to validate the target - * on initialisation. - */ - ASSERT(cfg != HardwareConfigMap.end()); - return cfg->second; -} diff --git a/src/ipa/raspberrypi/controller/controller.h b/src/ipa/raspberrypi/controller/controller.h deleted file mode 100644 index c6af5cd6..00000000 --- a/src/ipa/raspberrypi/controller/controller.h +++ /dev/null @@ -1,73 +0,0 @@ -/* SPDX-License-Identifier: BSD-2-Clause */ -/* - * Copyright (C) 2019, Raspberry Pi Ltd - * - * controller.h - ISP controller interface - */ -#pragma once - -/* - * The Controller is simply a container for a collecting together a number of - * "control algorithms" (such as AWB etc.) and for running them all in a - * convenient manner. - */ - -#include -#include - -#include "libcamera/internal/yaml_parser.h" - -#include "camera_mode.h" -#include "device_status.h" -#include "metadata.h" -#include "statistics.h" - -namespace RPiController { - -class Algorithm; -typedef std::unique_ptr AlgorithmPtr; - -/* - * The Controller holds a pointer to some global_metadata, which is how - * different controllers and control algorithms within them can exchange - * information. The Prepare function returns a pointer to metadata for this - * specific image, and which should be passed on to the Process function. - */ - -class Controller -{ -public: - struct HardwareConfig { - libcamera::Size agcRegions; - libcamera::Size agcZoneWeights; - libcamera::Size awbRegions; - libcamera::Size focusRegions; - unsigned int numHistogramBins; - unsigned int numGammaPoints; - unsigned int pipelineWidth; - }; - - Controller(); - ~Controller(); - int read(char const *filename); - void initialise(); - void switchMode(CameraMode const &cameraMode, Metadata *metadata); - void prepare(Metadata *imageMetadata); - void process(StatisticsPtr stats, Metadata *imageMetadata); - Metadata &getGlobalMetadata(); - Algorithm *getAlgorithm(std::string const &name) const; - const std::string &getTarget() const; - const HardwareConfig &getHardwareConfig() const; - -protected: - int createAlgorithm(const std::string &name, const libcamera::YamlObject ¶ms); - - Metadata globalMetadata_; - std::vector algorithms_; - bool switchModeCalled_; - -private: - std::string target_; -}; - -} /* namespace RPiController */ diff --git a/src/ipa/raspberrypi/controller/denoise_algorithm.h b/src/ipa/raspberrypi/controller/denoise_algorithm.h deleted file mode 100644 index 52009ba9..00000000 --- a/src/ipa/raspberrypi/controller/denoise_algorithm.h +++ /dev/null @@ -1,23 +0,0 @@ -/* SPDX-License-Identifier: BSD-2-Clause */ -/* - * Copyright (C) 2021, Raspberry Pi Ltd - * - * denoise.h - Denoise control algorithm interface - */ -#pragma once - -#include "algorithm.h" - -namespace RPiController { - -enum class DenoiseMode { Off, ColourOff, ColourFast, ColourHighQuality }; - -class DenoiseAlgorithm : public Algorithm -{ -public: - DenoiseAlgorithm(Controller *controller) : Algorithm(controller) {} - /* A Denoise algorithm must provide the following: */ - virtual void setMode(DenoiseMode mode) = 0; -}; - -} /* namespace RPiController */ diff --git a/src/ipa/raspberrypi/controller/denoise_status.h b/src/ipa/raspberrypi/controller/denoise_status.h deleted file mode 100644 index f6b9ee29..00000000 --- a/src/ipa/raspberrypi/controller/denoise_status.h +++ /dev/null @@ -1,16 +0,0 @@ -/* SPDX-License-Identifier: BSD-2-Clause */ -/* - * Copyright (C) 2019-2021, Raspberry Pi Ltd - * - * denoise_status.h - Denoise control algorithm status - */ -#pragma once - -/* This stores the parameters required for Denoise. */ - -struct DenoiseStatus { - double noiseConstant; - double noiseSlope; - double strength; - unsigned int mode; -}; diff --git a/src/ipa/raspberrypi/controller/device_status.cpp b/src/ipa/raspberrypi/controller/device_status.cpp deleted file mode 100644 index c907efdd..00000000 --- a/src/ipa/raspberrypi/controller/device_status.cpp +++ /dev/null @@ -1,31 +0,0 @@ -/* SPDX-License-Identifier: BSD-2-Clause */ -/* - * Copyright (C) 2021, Raspberry Pi Ltd - * - * device_status.cpp - device (image sensor) status - */ -#include "device_status.h" - -using namespace libcamera; /* for the Duration operator<< overload */ - -std::ostream &operator<<(std::ostream &out, const DeviceStatus &d) -{ - out << "Exposure: " << d.shutterSpeed - << " Frame length: " << d.frameLength - << " Line length: " << d.lineLength - << " Gain: " << d.analogueGain; - - if (d.aperture) - out << " Aperture: " << *d.aperture; - - if (d.lensPosition) - out << " Lens: " << *d.lensPosition; - - if (d.flashIntensity) - out << " Flash: " << *d.flashIntensity; - - if (d.sensorTemperature) - out << " Temperature: " << *d.sensorTemperature; - - return out; -} diff --git a/src/ipa/raspberrypi/controller/device_status.h b/src/ipa/raspberrypi/controller/device_status.h deleted file mode 100644 index c45db749..00000000 --- a/src/ipa/raspberrypi/controller/device_status.h +++ /dev/null @@ -1,43 +0,0 @@ -/* SPDX-License-Identifier: BSD-2-Clause */ -/* - * Copyright (C) 2019-2021, Raspberry Pi Ltd - * - * device_status.h - device (image sensor) status - */ -#pragma once - -#include -#include - -#include - -/* - * Definition of "device metadata" which stores things like shutter time and - * analogue gain that downstream control algorithms will want to know. - */ - -struct DeviceStatus { - DeviceStatus() - : shutterSpeed(std::chrono::seconds(0)), frameLength(0), - lineLength(std::chrono::seconds(0)), analogueGain(0.0) - { - } - - friend std::ostream &operator<<(std::ostream &out, const DeviceStatus &d); - - /* time shutter is open */ - libcamera::utils::Duration shutterSpeed; - /* frame length given in number of lines */ - uint32_t frameLength; - /* line length for the current frame */ - libcamera::utils::Duration lineLength; - double analogueGain; - /* 1.0/distance-in-metres */ - std::optional lensPosition; - /* 1/f so that brightness quadruples when this doubles */ - std::optional aperture; - /* proportional to brightness with 0 = no flash, 1 = maximum flash */ - std::optional flashIntensity; - /* Sensor reported temperature value (in degrees) */ - std::optional sensorTemperature; -}; diff --git a/src/ipa/raspberrypi/controller/dpc_status.h b/src/ipa/raspberrypi/controller/dpc_status.h deleted file mode 100644 index 46d0cf34..00000000 --- a/src/ipa/raspberrypi/controller/dpc_status.h +++ /dev/null @@ -1,13 +0,0 @@ -/* SPDX-License-Identifier: BSD-2-Clause */ -/* - * Copyright (C) 2019, Raspberry Pi Ltd - * - * dpc_status.h - DPC (defective pixel correction) control algorithm status - */ -#pragma once - -/* The "DPC" algorithm sets defective pixel correction strength. */ - -struct DpcStatus { - int strength; /* 0 = "off", 1 = "normal", 2 = "strong" */ -}; diff --git a/src/ipa/raspberrypi/controller/geq_status.h b/src/ipa/raspberrypi/controller/geq_status.h deleted file mode 100644 index 2d749fc9..00000000 --- a/src/ipa/raspberrypi/controller/geq_status.h +++ /dev/null @@ -1,14 +0,0 @@ -/* SPDX-License-Identifier: BSD-2-Clause */ -/* - * Copyright (C) 2019, Raspberry Pi Ltd - * - * geq_status.h - GEQ (green equalisation) control algorithm status - */ -#pragma once - -/* The "GEQ" algorithm calculates the green equalisation thresholds */ - -struct GeqStatus { - uint16_t offset; - double slope; -}; diff --git a/src/ipa/raspberrypi/controller/histogram.cpp b/src/ipa/raspberrypi/controller/histogram.cpp deleted file mode 100644 index 16a9207f..00000000 --- a/src/ipa/raspberrypi/controller/histogram.cpp +++ /dev/null @@ -1,64 +0,0 @@ -/* SPDX-License-Identifier: BSD-2-Clause */ -/* - * Copyright (C) 2019, Raspberry Pi Ltd - * - * histogram.cpp - histogram calculations - */ -#include -#include - -#include "histogram.h" - -using namespace RPiController; - -uint64_t Histogram::cumulativeFreq(double bin) const -{ - if (bin <= 0) - return 0; - else if (bin >= bins()) - return total(); - int b = (int)bin; - return cumulative_[b] + - (bin - b) * (cumulative_[b + 1] - cumulative_[b]); -} - -double Histogram::quantile(double q, int first, int last) const -{ - if (first == -1) - first = 0; - if (last == -1) - last = cumulative_.size() - 2; - assert(first <= last); - uint64_t items = q * total(); - while (first < last) /* binary search to find the right bin */ - { - int middle = (first + last) / 2; - if (cumulative_[middle + 1] > items) - last = middle; /* between first and middle */ - else - first = middle + 1; /* after middle */ - } - assert(items >= cumulative_[first] && items <= cumulative_[last + 1]); - double frac = cumulative_[first + 1] == cumulative_[first] ? 0 - : (double)(items - cumulative_[first]) / - (cumulative_[first + 1] - cumulative_[first]); - return first + frac; -} - -double Histogram::interQuantileMean(double qLo, double qHi) const -{ - assert(qHi > qLo); - double pLo = quantile(qLo); - double pHi = quantile(qHi, (int)pLo); - double sumBinFreq = 0, cumulFreq = 0; - for (double pNext = floor(pLo) + 1.0; pNext <= ceil(pHi); - pLo = pNext, pNext += 1.0) { - int bin = floor(pLo); - double freq = (cumulative_[bin + 1] - cumulative_[bin]) * - (std::min(pNext, pHi) - pLo); - sumBinFreq += bin * freq; - cumulFreq += freq; - } - /* add 0.5 to give an average for bin mid-points */ - return sumBinFreq / cumulFreq + 0.5; -} diff --git a/src/ipa/raspberrypi/controller/histogram.h b/src/ipa/raspberrypi/controller/histogram.h deleted file mode 100644 index 6b3e3a9e..00000000 --- a/src/ipa/raspberrypi/controller/histogram.h +++ /dev/null @@ -1,53 +0,0 @@ -/* SPDX-License-Identifier: BSD-2-Clause */ -/* - * Copyright (C) 2019, Raspberry Pi Ltd - * - * histogram.h - histogram calculation interface - */ -#pragma once - -#include -#include -#include - -/* - * A simple histogram class, for use in particular to find "quantiles" and - * averages between "quantiles". - */ - -namespace RPiController { - -class Histogram -{ -public: - Histogram() - { - cumulative_.push_back(0); - } - - template Histogram(T *histogram, int num) - { - assert(num); - cumulative_.reserve(num + 1); - cumulative_.push_back(0); - for (int i = 0; i < num; i++) - cumulative_.push_back(cumulative_.back() + - histogram[i]); - } - uint32_t bins() const { return cumulative_.size() - 1; } - uint64_t total() const { return cumulative_[cumulative_.size() - 1]; } - /* Cumulative frequency up to a (fractional) point in a bin. */ - uint64_t cumulativeFreq(double bin) const; - /* - * Return the (fractional) bin of the point q (0 <= q <= 1) through the - * histogram. Optionally provide limits to help. - */ - double quantile(double q, int first = -1, int last = -1) const; - /* Return the average histogram bin value between the two quantiles. */ - double interQuantileMean(double qLo, double qHi) const; - -private: - std::vector cumulative_; -}; - -} /* namespace RPiController */ diff --git a/src/ipa/raspberrypi/controller/lux_status.h b/src/ipa/raspberrypi/controller/lux_status.h deleted file mode 100644 index 5eb9faac..00000000 --- a/src/ipa/raspberrypi/controller/lux_status.h +++ /dev/null @@ -1,23 +0,0 @@ -/* SPDX-License-Identifier: BSD-2-Clause */ -/* - * Copyright (C) 2019, Raspberry Pi Ltd - * - * lux_status.h - Lux control algorithm status - */ -#pragma once - -/* - * The "lux" algorithm looks at the (AGC) histogram statistics of the frame and - * estimates the current lux level of the scene. It does this by a simple ratio - * calculation comparing to a reference image that was taken in known conditions - * with known statistics and a properly measured lux level. There is a slight - * problem with aperture, in that it may be variable without the system knowing - * or being aware of it. In this case an external application may set a - * "current_aperture" value if it wishes, which would be used in place of the - * (presumably meaningless) value in the image metadata. - */ - -struct LuxStatus { - double lux; - double aperture; -}; diff --git a/src/ipa/raspberrypi/controller/metadata.h b/src/ipa/raspberrypi/controller/metadata.h deleted file mode 100644 index bf8a2393..00000000 --- a/src/ipa/raspberrypi/controller/metadata.h +++ /dev/null @@ -1,126 +0,0 @@ -/* SPDX-License-Identifier: BSD-2-Clause */ -/* - * Copyright (C) 2019-2021, Raspberry Pi Ltd - * - * metadata.h - general metadata class - */ -#pragma once - -/* A simple class for carrying arbitrary metadata, for example about an image. */ - -#include -#include -#include -#include - -#include - -namespace RPiController { - -class LIBCAMERA_TSA_CAPABILITY("mutex") Metadata -{ -public: - Metadata() = default; - - Metadata(Metadata const &other) - { - std::scoped_lock otherLock(other.mutex_); - data_ = other.data_; - } - - Metadata(Metadata &&other) - { - std::scoped_lock otherLock(other.mutex_); - data_ = std::move(other.data_); - other.data_.clear(); - } - - template - void set(std::string const &tag, T const &value) - { - std::scoped_lock lock(mutex_); - data_[tag] = value; - } - - template - int get(std::string const &tag, T &value) const - { - std::scoped_lock lock(mutex_); - auto it = data_.find(tag); - if (it == data_.end()) - return -1; - value = std::any_cast(it->second); - return 0; - } - - void clear() - { - std::scoped_lock lock(mutex_); - data_.clear(); - } - - Metadata &operator=(Metadata const &other) - { - std::scoped_lock lock(mutex_, other.mutex_); - data_ = other.data_; - return *this; - } - - Metadata &operator=(Metadata &&other) - { - std::scoped_lock lock(mutex_, other.mutex_); - data_ = std::move(other.data_); - other.data_.clear(); - return *this; - } - - void merge(Metadata &other) - { - std::scoped_lock lock(mutex_, other.mutex_); - data_.merge(other.data_); - } - - void mergeCopy(const Metadata &other) - { - std::scoped_lock lock(mutex_, other.mutex_); - /* - * If the metadata key exists, ignore this item and copy only - * unique key/value pairs. - */ - data_.insert(other.data_.begin(), other.data_.end()); - } - - template - T *getLocked(std::string const &tag) - { - /* - * This allows in-place access to the Metadata contents, - * for which you should be holding the lock. - */ - auto it = data_.find(tag); - if (it == data_.end()) - return nullptr; - return std::any_cast(&it->second); - } - - template - void setLocked(std::string const &tag, T const &value) - { - /* Use this only if you're holding the lock yourself. */ - data_[tag] = value; - } - - /* - * Note: use of (lowercase) lock and unlock means you can create scoped - * locks with the standard lock classes. - * e.g. std::lock_guard lock(metadata) - */ - void lock() LIBCAMERA_TSA_ACQUIRE() { mutex_.lock(); } - void unlock() LIBCAMERA_TSA_RELEASE() { mutex_.unlock(); } - -private: - mutable std::mutex mutex_; - std::map data_; -}; - -} /* namespace RPiController */ diff --git a/src/ipa/raspberrypi/controller/noise_status.h b/src/ipa/raspberrypi/controller/noise_status.h deleted file mode 100644 index da194f71..00000000 --- a/src/ipa/raspberrypi/controller/noise_status.h +++ /dev/null @@ -1,14 +0,0 @@ -/* SPDX-License-Identifier: BSD-2-Clause */ -/* - * Copyright (C) 2019, Raspberry Pi Ltd - * - * noise_status.h - Noise control algorithm status - */ -#pragma once - -/* The "noise" algorithm stores an estimate of the noise profile for this image. */ - -struct NoiseStatus { - double noiseConstant; - double noiseSlope; -}; diff --git a/src/ipa/raspberrypi/controller/pdaf_data.h b/src/ipa/raspberrypi/controller/pdaf_data.h deleted file mode 100644 index 470510f2..00000000 --- a/src/ipa/raspberrypi/controller/pdaf_data.h +++ /dev/null @@ -1,24 +0,0 @@ -/* SPDX-License-Identifier: BSD-2-Clause */ -/* - * Copyright (C) 2022, Raspberry Pi Ltd - * - * pdaf_data.h - PDAF Metadata - */ -#pragma once - -#include - -#include "region_stats.h" - -namespace RPiController { - -struct PdafData { - /* Confidence, in arbitrary units */ - uint16_t conf; - /* Phase error, in s16 Q4 format (S.11.4) */ - int16_t phase; -}; - -using PdafRegions = RegionStats; - -} /* namespace RPiController */ diff --git a/src/ipa/raspberrypi/controller/pwl.cpp b/src/ipa/raspberrypi/controller/pwl.cpp deleted file mode 100644 index 70c2e24b..00000000 --- a/src/ipa/raspberrypi/controller/pwl.cpp +++ /dev/null @@ -1,269 +0,0 @@ -/* SPDX-License-Identifier: BSD-2-Clause */ -/* - * Copyright (C) 2019, Raspberry Pi Ltd - * - * pwl.cpp - piecewise linear functions - */ - -#include -#include -#include - -#include "pwl.h" - -using namespace RPiController; - -int Pwl::read(const libcamera::YamlObject ¶ms) -{ - if (!params.size() || params.size() % 2) - return -EINVAL; - - const auto &list = params.asList(); - - for (auto it = list.begin(); it != list.end(); it++) { - auto x = it->get(); - if (!x) - return -EINVAL; - if (it != list.begin() && *x <= points_.back().x) - return -EINVAL; - - auto y = (++it)->get(); - if (!y) - return -EINVAL; - - points_.push_back(Point(*x, *y)); - } - - return 0; -} - -void Pwl::append(double x, double y, const double eps) -{ - if (points_.empty() || points_.back().x + eps < x) - points_.push_back(Point(x, y)); -} - -void Pwl::prepend(double x, double y, const double eps) -{ - if (points_.empty() || points_.front().x - eps > x) - points_.insert(points_.begin(), Point(x, y)); -} - -Pwl::Interval Pwl::domain() const -{ - return Interval(points_[0].x, points_[points_.size() - 1].x); -} - -Pwl::Interval Pwl::range() const -{ - double lo = points_[0].y, hi = lo; - for (auto &p : points_) - lo = std::min(lo, p.y), hi = std::max(hi, p.y); - return Interval(lo, hi); -} - -bool Pwl::empty() const -{ - return points_.empty(); -} - -double Pwl::eval(double x, int *spanPtr, bool updateSpan) const -{ - int span = findSpan(x, spanPtr && *spanPtr != -1 ? *spanPtr : points_.size() / 2 - 1); - if (spanPtr && updateSpan) - *spanPtr = span; - return points_[span].y + - (x - points_[span].x) * (points_[span + 1].y - points_[span].y) / - (points_[span + 1].x - points_[span].x); -} - -int Pwl::findSpan(double x, int span) const -{ - /* - * Pwls are generally small, so linear search may well be faster than - * binary, though could review this if large PWls start turning up. - */ - int lastSpan = points_.size() - 2; - /* - * some algorithms may call us with span pointing directly at the last - * control point - */ - span = std::max(0, std::min(lastSpan, span)); - while (span < lastSpan && x >= points_[span + 1].x) - span++; - while (span && x < points_[span].x) - span--; - return span; -} - -Pwl::PerpType Pwl::invert(Point const &xy, Point &perp, int &span, - const double eps) const -{ - assert(span >= -1); - bool prevOffEnd = false; - for (span = span + 1; span < (int)points_.size() - 1; span++) { - Point spanVec = points_[span + 1] - points_[span]; - double t = ((xy - points_[span]) % spanVec) / spanVec.len2(); - if (t < -eps) /* off the start of this span */ - { - if (span == 0) { - perp = points_[span]; - return PerpType::Start; - } else if (prevOffEnd) { - perp = points_[span]; - return PerpType::Vertex; - } - } else if (t > 1 + eps) /* off the end of this span */ - { - if (span == (int)points_.size() - 2) { - perp = points_[span + 1]; - return PerpType::End; - } - prevOffEnd = true; - } else /* a true perpendicular */ - { - perp = points_[span] + spanVec * t; - return PerpType::Perpendicular; - } - } - return PerpType::None; -} - -Pwl Pwl::inverse(bool *trueInverse, const double eps) const -{ - bool appended = false, prepended = false, neither = false; - Pwl inverse; - - for (Point const &p : points_) { - if (inverse.empty()) - inverse.append(p.y, p.x, eps); - else if (std::abs(inverse.points_.back().x - p.y) <= eps || - std::abs(inverse.points_.front().x - p.y) <= eps) - /* do nothing */; - else if (p.y > inverse.points_.back().x) { - inverse.append(p.y, p.x, eps); - appended = true; - } else if (p.y < inverse.points_.front().x) { - inverse.prepend(p.y, p.x, eps); - prepended = true; - } else - neither = true; - } - - /* - * This is not a proper inverse if we found ourselves putting points - * onto both ends of the inverse, or if there were points that couldn't - * go on either. - */ - if (trueInverse) - *trueInverse = !(neither || (appended && prepended)); - - return inverse; -} - -Pwl Pwl::compose(Pwl const &other, const double eps) const -{ - double thisX = points_[0].x, thisY = points_[0].y; - int thisSpan = 0, otherSpan = other.findSpan(thisY, 0); - Pwl result({ { thisX, other.eval(thisY, &otherSpan, false) } }); - while (thisSpan != (int)points_.size() - 1) { - double dx = points_[thisSpan + 1].x - points_[thisSpan].x, - dy = points_[thisSpan + 1].y - points_[thisSpan].y; - if (std::abs(dy) > eps && - otherSpan + 1 < (int)other.points_.size() && - points_[thisSpan + 1].y >= - other.points_[otherSpan + 1].x + eps) { - /* - * next control point in result will be where this - * function's y reaches the next span in other - */ - thisX = points_[thisSpan].x + - (other.points_[otherSpan + 1].x - - points_[thisSpan].y) * - dx / dy; - thisY = other.points_[++otherSpan].x; - } else if (std::abs(dy) > eps && otherSpan > 0 && - points_[thisSpan + 1].y <= - other.points_[otherSpan - 1].x - eps) { - /* - * next control point in result will be where this - * function's y reaches the previous span in other - */ - thisX = points_[thisSpan].x + - (other.points_[otherSpan + 1].x - - points_[thisSpan].y) * - dx / dy; - thisY = other.points_[--otherSpan].x; - } else { - /* we stay in the same span in other */ - thisSpan++; - thisX = points_[thisSpan].x, - thisY = points_[thisSpan].y; - } - result.append(thisX, other.eval(thisY, &otherSpan, false), - eps); - } - return result; -} - -void Pwl::map(std::function f) const -{ - for (auto &pt : points_) - f(pt.x, pt.y); -} - -void Pwl::map2(Pwl const &pwl0, Pwl const &pwl1, - std::function f) -{ - int span0 = 0, span1 = 0; - double x = std::min(pwl0.points_[0].x, pwl1.points_[0].x); - f(x, pwl0.eval(x, &span0, false), pwl1.eval(x, &span1, false)); - while (span0 < (int)pwl0.points_.size() - 1 || - span1 < (int)pwl1.points_.size() - 1) { - if (span0 == (int)pwl0.points_.size() - 1) - x = pwl1.points_[++span1].x; - else if (span1 == (int)pwl1.points_.size() - 1) - x = pwl0.points_[++span0].x; - else if (pwl0.points_[span0 + 1].x > pwl1.points_[span1 + 1].x) - x = pwl1.points_[++span1].x; - else - x = pwl0.points_[++span0].x; - f(x, pwl0.eval(x, &span0, false), pwl1.eval(x, &span1, false)); - } -} - -Pwl Pwl::combine(Pwl const &pwl0, Pwl const &pwl1, - std::function f, - const double eps) -{ - Pwl result; - map2(pwl0, pwl1, [&](double x, double y0, double y1) { - result.append(x, f(x, y0, y1), eps); - }); - return result; -} - -void Pwl::matchDomain(Interval const &domain, bool clip, const double eps) -{ - int span = 0; - prepend(domain.start, eval(clip ? points_[0].x : domain.start, &span), - eps); - span = points_.size() - 2; - append(domain.end, eval(clip ? points_.back().x : domain.end, &span), - eps); -} - -Pwl &Pwl::operator*=(double d) -{ - for (auto &pt : points_) - pt.y *= d; - return *this; -} - -void Pwl::debug(FILE *fp) const -{ - fprintf(fp, "Pwl {\n"); - for (auto &p : points_) - fprintf(fp, "\t(%g, %g)\n", p.x, p.y); - fprintf(fp, "}\n"); -} diff --git a/src/ipa/raspberrypi/controller/pwl.h b/src/ipa/raspberrypi/controller/pwl.h deleted file mode 100644 index aacf6039..00000000 --- a/src/ipa/raspberrypi/controller/pwl.h +++ /dev/null @@ -1,127 +0,0 @@ -/* SPDX-License-Identifier: BSD-2-Clause */ -/* - * Copyright (C) 2019, Raspberry Pi Ltd - * - * pwl.h - piecewise linear functions interface - */ -#pragma once - -#include -#include -#include - -#include "libcamera/internal/yaml_parser.h" - -namespace RPiController { - -class Pwl -{ -public: - struct Interval { - Interval(double _start, double _end) - : start(_start), end(_end) - { - } - double start, end; - bool contains(double value) - { - return value >= start && value <= end; - } - double clip(double value) - { - return value < start ? start - : (value > end ? end : value); - } - double len() const { return end - start; } - }; - struct Point { - Point() : x(0), y(0) {} - Point(double _x, double _y) - : x(_x), y(_y) {} - double x, y; - Point operator-(Point const &p) const - { - return Point(x - p.x, y - p.y); - } - Point operator+(Point const &p) const - { - return Point(x + p.x, y + p.y); - } - double operator%(Point const &p) const - { - return x * p.x + y * p.y; - } - Point operator*(double f) const { return Point(x * f, y * f); } - Point operator/(double f) const { return Point(x / f, y / f); } - double len2() const { return x * x + y * y; } - double len() const { return sqrt(len2()); } - }; - Pwl() {} - Pwl(std::vector const &points) : points_(points) {} - int read(const libcamera::YamlObject ¶ms); - void append(double x, double y, const double eps = 1e-6); - void prepend(double x, double y, const double eps = 1e-6); - Interval domain() const; - Interval range() const; - bool empty() const; - /* - * Evaluate Pwl, optionally supplying an initial guess for the - * "span". The "span" may be optionally be updated. If you want to know - * the "span" value but don't have an initial guess you can set it to - * -1. - */ - double eval(double x, int *spanPtr = nullptr, - bool updateSpan = true) const; - /* - * Find perpendicular closest to xy, starting from span+1 so you can - * call it repeatedly to check for multiple closest points (set span to - * -1 on the first call). Also returns "pseudo" perpendiculars; see - * PerpType enum. - */ - enum class PerpType { - None, /* no perpendicular found */ - Start, /* start of Pwl is closest point */ - End, /* end of Pwl is closest point */ - Vertex, /* vertex of Pwl is closest point */ - Perpendicular /* true perpendicular found */ - }; - PerpType invert(Point const &xy, Point &perp, int &span, - const double eps = 1e-6) const; - /* - * Compute the inverse function. Indicate if it is a proper (true) - * inverse, or only a best effort (e.g. input was non-monotonic). - */ - Pwl inverse(bool *trueInverse = nullptr, const double eps = 1e-6) const; - /* Compose two Pwls together, doing "this" first and "other" after. */ - Pwl compose(Pwl const &other, const double eps = 1e-6) const; - /* Apply function to (x,y) values at every control point. */ - void map(std::function f) const; - /* - * Apply function to (x, y0, y1) values wherever either Pwl has a - * control point. - */ - static void map2(Pwl const &pwl0, Pwl const &pwl1, - std::function f); - /* - * Combine two Pwls, meaning we create a new Pwl where the y values are - * given by running f wherever either has a knot. - */ - static Pwl - combine(Pwl const &pwl0, Pwl const &pwl1, - std::function f, - const double eps = 1e-6); - /* - * Make "this" match (at least) the given domain. Any extension my be - * clipped or linear. - */ - void matchDomain(Interval const &domain, bool clip = true, - const double eps = 1e-6); - Pwl &operator*=(double d); - void debug(FILE *fp = stdout) const; - -private: - int findSpan(double x, int span) const; - std::vector points_; -}; - -} /* namespace RPiController */ diff --git a/src/ipa/raspberrypi/controller/region_stats.h b/src/ipa/raspberrypi/controller/region_stats.h deleted file mode 100644 index a8860dc8..00000000 --- a/src/ipa/raspberrypi/controller/region_stats.h +++ /dev/null @@ -1,123 +0,0 @@ -/* SPDX-License-Identifier: BSD-2-Clause */ -/* - * Copyright (C) 2022, Raspberry Pi Ltd - * - * region_stats.h - Raspberry Pi region based statistics container - */ -#pragma once - -#include -#include -#include - -#include - -namespace RPiController { - -template -class RegionStats -{ -public: - struct Region { - T val; - uint32_t counted; - uint32_t uncounted; - }; - - RegionStats() - : size_({}), numFloating_(0), default_({}) - { - } - - void init(const libcamera::Size &size, unsigned int numFloating = 0) - { - size_ = size; - numFloating_ = numFloating; - regions_.clear(); - regions_.resize(size_.width * size_.height + numFloating_); - } - - void init(unsigned int num) - { - size_ = libcamera::Size(num, 1); - numFloating_ = 0; - regions_.clear(); - regions_.resize(num); - } - - unsigned int numRegions() const - { - return size_.width * size_.height; - } - - unsigned int numFloatingRegions() const - { - return numFloating_; - } - - libcamera::Size size() const - { - return size_; - } - - void set(unsigned int index, const Region ®ion) - { - if (index >= numRegions()) - return; - set_(index, region); - } - - void set(const libcamera::Point &pos, const Region ®ion) - { - set(pos.y * size_.width + pos.x, region); - } - - void setFloating(unsigned int index, const Region ®ion) - { - if (index >= numFloatingRegions()) - return; - set(numRegions() + index, region); - } - - const Region &get(unsigned int index) const - { - if (index >= numRegions()) - return default_; - return get_(index); - } - - const Region &get(const libcamera::Point &pos) const - { - return get(pos.y * size_.width + pos.x); - } - - const Region &getFloating(unsigned int index) const - { - if (index >= numFloatingRegions()) - return default_; - return get_(numRegions() + index); - } - - typename std::vector::iterator begin() { return regions_.begin(); } - typename std::vector::iterator end() { return regions_.end(); } - typename std::vector::const_iterator begin() const { return regions_.begin(); } - typename std::vector::const_iterator end() const { return regions_.end(); } - -private: - void set_(unsigned int index, const Region ®ion) - { - regions_[index] = region; - } - - const Region &get_(unsigned int index) const - { - return regions_[index]; - } - - libcamera::Size size_; - unsigned int numFloating_; - std::vector regions_; - Region default_; -}; - -} /* namespace RPiController */ diff --git a/src/ipa/raspberrypi/controller/rpi/af.cpp b/src/ipa/raspberrypi/controller/rpi/af.cpp deleted file mode 100644 index ed0c8a94..00000000 --- a/src/ipa/raspberrypi/controller/rpi/af.cpp +++ /dev/null @@ -1,797 +0,0 @@ -/* SPDX-License-Identifier: BSD-2-Clause */ -/* - * Copyright (C) 2022-2023, Raspberry Pi Ltd - * - * af.cpp - Autofocus control algorithm - */ - -#include "af.h" - -#include -#include -#include - -#include - -#include - -using namespace RPiController; -using namespace libcamera; - -LOG_DEFINE_CATEGORY(RPiAf) - -#define NAME "rpi.af" - -/* - * Default values for parameters. All may be overridden in the tuning file. - * Many of these values are sensor- or module-dependent; the defaults here - * assume IMX708 in a Raspberry Pi V3 camera with the standard lens. - * - * Here all focus values are in dioptres (1/m). They are converted to hardware - * units when written to status.lensSetting or returned from setLensPosition(). - * - * Gain and delay values are relative to the update rate, since much (not all) - * of the delay is in the sensor and (for CDAF) ISP, not the lens mechanism; - * but note that algorithms are updated at no more than 30 Hz. - */ - -Af::RangeDependentParams::RangeDependentParams() - : focusMin(0.0), - focusMax(12.0), - focusDefault(1.0) -{ -} - -Af::SpeedDependentParams::SpeedDependentParams() - : stepCoarse(1.0), - stepFine(0.25), - contrastRatio(0.75), - pdafGain(-0.02), - pdafSquelch(0.125), - maxSlew(2.0), - pdafFrames(20), - dropoutFrames(6), - stepFrames(4) -{ -} - -Af::CfgParams::CfgParams() - : confEpsilon(8), - confThresh(16), - confClip(512), - skipFrames(5), - map() -{ -} - -template -static void readNumber(T &dest, const libcamera::YamlObject ¶ms, char const *name) -{ - auto value = params[name].get(); - if (value) - dest = *value; - else - LOG(RPiAf, Warning) << "Missing parameter \"" << name << "\""; -} - -void Af::RangeDependentParams::read(const libcamera::YamlObject ¶ms) -{ - - readNumber(focusMin, params, "min"); - readNumber(focusMax, params, "max"); - readNumber(focusDefault, params, "default"); -} - -void Af::SpeedDependentParams::read(const libcamera::YamlObject ¶ms) -{ - readNumber(stepCoarse, params, "step_coarse"); - readNumber(stepFine, params, "step_fine"); - readNumber(contrastRatio, params, "contrast_ratio"); - readNumber(pdafGain, params, "pdaf_gain"); - readNumber(pdafSquelch, params, "pdaf_squelch"); - readNumber(maxSlew, params, "max_slew"); - readNumber(pdafFrames, params, "pdaf_frames"); - readNumber(dropoutFrames, params, "dropout_frames"); - readNumber(stepFrames, params, "step_frames"); -} - -int Af::CfgParams::read(const libcamera::YamlObject ¶ms) -{ - if (params.contains("ranges")) { - auto &rr = params["ranges"]; - - if (rr.contains("normal")) - ranges[AfRangeNormal].read(rr["normal"]); - else - LOG(RPiAf, Warning) << "Missing range \"normal\""; - - ranges[AfRangeMacro] = ranges[AfRangeNormal]; - if (rr.contains("macro")) - ranges[AfRangeMacro].read(rr["macro"]); - - ranges[AfRangeFull].focusMin = std::min(ranges[AfRangeNormal].focusMin, - ranges[AfRangeMacro].focusMin); - ranges[AfRangeFull].focusMax = std::max(ranges[AfRangeNormal].focusMax, - ranges[AfRangeMacro].focusMax); - ranges[AfRangeFull].focusDefault = ranges[AfRangeNormal].focusDefault; - if (rr.contains("full")) - ranges[AfRangeFull].read(rr["full"]); - } else - LOG(RPiAf, Warning) << "No ranges defined"; - - if (params.contains("speeds")) { - auto &ss = params["speeds"]; - - if (ss.contains("normal")) - speeds[AfSpeedNormal].read(ss["normal"]); - else - LOG(RPiAf, Warning) << "Missing speed \"normal\""; - - speeds[AfSpeedFast] = speeds[AfSpeedNormal]; - if (ss.contains("fast")) - speeds[AfSpeedFast].read(ss["fast"]); - } else - LOG(RPiAf, Warning) << "No speeds defined"; - - readNumber(confEpsilon, params, "conf_epsilon"); - readNumber(confThresh, params, "conf_thresh"); - readNumber(confClip, params, "conf_clip"); - readNumber(skipFrames, params, "skip_frames"); - - if (params.contains("map")) - map.read(params["map"]); - else - LOG(RPiAf, Warning) << "No map defined"; - - return 0; -} - -void Af::CfgParams::initialise() -{ - if (map.empty()) { - /* Default mapping from dioptres to hardware setting */ - static constexpr double DefaultMapX0 = 0.0; - static constexpr double DefaultMapY0 = 445.0; - static constexpr double DefaultMapX1 = 15.0; - static constexpr double DefaultMapY1 = 925.0; - - map.append(DefaultMapX0, DefaultMapY0); - map.append(DefaultMapX1, DefaultMapY1); - } -} - -/* Af Algorithm class */ - -static constexpr unsigned MaxWindows = 10; - -Af::Af(Controller *controller) - : AfAlgorithm(controller), - cfg_(), - range_(AfRangeNormal), - speed_(AfSpeedNormal), - mode_(AfAlgorithm::AfModeManual), - pauseFlag_(false), - statsRegion_(0, 0, 0, 0), - windows_(), - useWindows_(false), - phaseWeights_(), - contrastWeights_(), - scanState_(ScanState::Idle), - initted_(false), - ftarget_(-1.0), - fsmooth_(-1.0), - prevContrast_(0.0), - skipCount_(0), - stepCount_(0), - dropCount_(0), - scanMaxContrast_(0.0), - scanMinContrast_(1.0e9), - scanData_(), - reportState_(AfState::Idle) -{ - /* - * Reserve space for data, to reduce memory fragmentation. It's too early - * to query the size of the PDAF (from camera) and Contrast (from ISP) - * statistics, but these are plausible upper bounds. - */ - phaseWeights_.w.reserve(16 * 12); - contrastWeights_.w.reserve(getHardwareConfig().focusRegions.width * - getHardwareConfig().focusRegions.height); - scanData_.reserve(32); -} - -Af::~Af() -{ -} - -char const *Af::name() const -{ - return NAME; -} - -int Af::read(const libcamera::YamlObject ¶ms) -{ - return cfg_.read(params); -} - -void Af::initialise() -{ - cfg_.initialise(); -} - -void Af::switchMode(CameraMode const &cameraMode, [[maybe_unused]] Metadata *metadata) -{ - (void)metadata; - - /* Assume that PDAF and Focus stats grids cover the visible area */ - statsRegion_.x = (int)cameraMode.cropX; - statsRegion_.y = (int)cameraMode.cropY; - statsRegion_.width = (unsigned)(cameraMode.width * cameraMode.scaleX); - statsRegion_.height = (unsigned)(cameraMode.height * cameraMode.scaleY); - LOG(RPiAf, Debug) << "switchMode: statsRegion: " - << statsRegion_.x << ',' - << statsRegion_.y << ',' - << statsRegion_.width << ',' - << statsRegion_.height; - invalidateWeights(); - - if (scanState_ >= ScanState::Coarse && scanState_ < ScanState::Settle) { - /* - * If a scan was in progress, re-start it, as CDAF statistics - * may have changed. Though if the application is just about - * to take a still picture, this will not help... - */ - startProgrammedScan(); - } - skipCount_ = cfg_.skipFrames; -} - -void Af::computeWeights(RegionWeights *wgts, unsigned rows, unsigned cols) -{ - wgts->rows = rows; - wgts->cols = cols; - wgts->sum = 0; - wgts->w.resize(rows * cols); - std::fill(wgts->w.begin(), wgts->w.end(), 0); - - if (rows > 0 && cols > 0 && useWindows_ && - statsRegion_.height >= rows && statsRegion_.width >= cols) { - /* - * Here we just merge all of the given windows, weighted by area. - * \todo Perhaps a better approach might be to find the phase in each - * window and choose either the closest or the highest-confidence one? - * Ensure weights sum to less than (1<<16). 46080 is a "round number" - * below 65536, for better rounding when window size is a simple - * fraction of image dimensions. - */ - const unsigned maxCellWeight = 46080u / (MaxWindows * rows * cols); - const unsigned cellH = statsRegion_.height / rows; - const unsigned cellW = statsRegion_.width / cols; - const unsigned cellA = cellH * cellW; - - for (auto &w : windows_) { - for (unsigned r = 0; r < rows; ++r) { - int y0 = std::max(statsRegion_.y + (int)(cellH * r), w.y); - int y1 = std::min(statsRegion_.y + (int)(cellH * (r + 1)), - w.y + (int)(w.height)); - if (y0 >= y1) - continue; - y1 -= y0; - for (unsigned c = 0; c < cols; ++c) { - int x0 = std::max(statsRegion_.x + (int)(cellW * c), w.x); - int x1 = std::min(statsRegion_.x + (int)(cellW * (c + 1)), - w.x + (int)(w.width)); - if (x0 >= x1) - continue; - unsigned a = y1 * (x1 - x0); - a = (maxCellWeight * a + cellA - 1) / cellA; - wgts->w[r * cols + c] += a; - wgts->sum += a; - } - } - } - } - - if (wgts->sum == 0) { - /* Default AF window is the middle 1/2 width of the middle 1/3 height */ - for (unsigned r = rows / 3; r < rows - rows / 3; ++r) { - for (unsigned c = cols / 4; c < cols - cols / 4; ++c) { - wgts->w[r * cols + c] = 1; - wgts->sum += 1; - } - } - } -} - -void Af::invalidateWeights() -{ - phaseWeights_.sum = 0; - contrastWeights_.sum = 0; -} - -bool Af::getPhase(PdafRegions const ®ions, double &phase, double &conf) -{ - libcamera::Size size = regions.size(); - if (size.height != phaseWeights_.rows || size.width != phaseWeights_.cols || - phaseWeights_.sum == 0) { - LOG(RPiAf, Debug) << "Recompute Phase weights " << size.width << 'x' << size.height; - computeWeights(&phaseWeights_, size.height, size.width); - } - - uint32_t sumWc = 0; - int64_t sumWcp = 0; - for (unsigned i = 0; i < regions.numRegions(); ++i) { - unsigned w = phaseWeights_.w[i]; - if (w) { - const PdafData &data = regions.get(i).val; - unsigned c = data.conf; - if (c >= cfg_.confThresh) { - if (c > cfg_.confClip) - c = cfg_.confClip; - c -= (cfg_.confThresh >> 2); - sumWc += w * c; - c -= (cfg_.confThresh >> 2); - sumWcp += (int64_t)(w * c) * (int64_t)data.phase; - } - } - } - - if (0 < phaseWeights_.sum && phaseWeights_.sum <= sumWc) { - phase = (double)sumWcp / (double)sumWc; - conf = (double)sumWc / (double)phaseWeights_.sum; - return true; - } else { - phase = 0.0; - conf = 0.0; - return false; - } -} - -double Af::getContrast(const FocusRegions &focusStats) -{ - libcamera::Size size = focusStats.size(); - if (size.height != contrastWeights_.rows || - size.width != contrastWeights_.cols || contrastWeights_.sum == 0) { - LOG(RPiAf, Debug) << "Recompute Contrast weights " - << size.width << 'x' << size.height; - computeWeights(&contrastWeights_, size.height, size.width); - } - - uint64_t sumWc = 0; - for (unsigned i = 0; i < focusStats.numRegions(); ++i) - sumWc += contrastWeights_.w[i] * focusStats.get(i).val; - - return (contrastWeights_.sum > 0) ? ((double)sumWc / (double)contrastWeights_.sum) : 0.0; -} - -void Af::doPDAF(double phase, double conf) -{ - /* Apply loop gain */ - phase *= cfg_.speeds[speed_].pdafGain; - - if (mode_ == AfModeContinuous) { - /* - * PDAF in Continuous mode. Scale down lens movement when - * delta is small or confidence is low, to suppress wobble. - */ - phase *= conf / (conf + cfg_.confEpsilon); - if (std::abs(phase) < cfg_.speeds[speed_].pdafSquelch) { - double a = phase / cfg_.speeds[speed_].pdafSquelch; - phase *= a * a; - } - } else { - /* - * PDAF in triggered-auto mode. Allow early termination when - * phase delta is small; scale down lens movements towards - * the end of the sequence, to ensure a stable image. - */ - if (stepCount_ >= cfg_.speeds[speed_].stepFrames) { - if (std::abs(phase) < cfg_.speeds[speed_].pdafSquelch) - stepCount_ = cfg_.speeds[speed_].stepFrames; - } else - phase *= stepCount_ / cfg_.speeds[speed_].stepFrames; - } - - /* Apply slew rate limit. Report failure if out of bounds. */ - if (phase < -cfg_.speeds[speed_].maxSlew) { - phase = -cfg_.speeds[speed_].maxSlew; - reportState_ = (ftarget_ <= cfg_.ranges[range_].focusMin) ? AfState::Failed - : AfState::Scanning; - } else if (phase > cfg_.speeds[speed_].maxSlew) { - phase = cfg_.speeds[speed_].maxSlew; - reportState_ = (ftarget_ >= cfg_.ranges[range_].focusMax) ? AfState::Failed - : AfState::Scanning; - } else - reportState_ = AfState::Focused; - - ftarget_ = fsmooth_ + phase; -} - -bool Af::earlyTerminationByPhase(double phase) -{ - if (scanData_.size() > 0 && - scanData_[scanData_.size() - 1].conf >= cfg_.confEpsilon) { - double oldFocus = scanData_[scanData_.size() - 1].focus; - double oldPhase = scanData_[scanData_.size() - 1].phase; - - /* - * Check that the gradient is finite and has the expected sign; - * Interpolate/extrapolate the lens position for zero phase. - * Check that the extrapolation is well-conditioned. - */ - if ((ftarget_ - oldFocus) * (phase - oldPhase) > 0.0) { - double param = phase / (phase - oldPhase); - if (-3.0 <= param && param <= 3.5) { - ftarget_ += param * (oldFocus - ftarget_); - LOG(RPiAf, Debug) << "ETBP: param=" << param; - return true; - } - } - } - - return false; -} - -double Af::findPeak(unsigned i) const -{ - double f = scanData_[i].focus; - - if (i > 0 && i + 1 < scanData_.size()) { - double dropLo = scanData_[i].contrast - scanData_[i - 1].contrast; - double dropHi = scanData_[i].contrast - scanData_[i + 1].contrast; - if (0.0 <= dropLo && dropLo < dropHi) { - double param = 0.3125 * (1.0 - dropLo / dropHi) * (1.6 - dropLo / dropHi); - f += param * (scanData_[i - 1].focus - f); - } else if (0.0 <= dropHi && dropHi < dropLo) { - double param = 0.3125 * (1.0 - dropHi / dropLo) * (1.6 - dropHi / dropLo); - f += param * (scanData_[i + 1].focus - f); - } - } - - LOG(RPiAf, Debug) << "FindPeak: " << f; - return f; -} - -void Af::doScan(double contrast, double phase, double conf) -{ - /* Record lens position, contrast and phase values for the current scan */ - if (scanData_.empty() || contrast > scanMaxContrast_) { - scanMaxContrast_ = contrast; - scanMaxIndex_ = scanData_.size(); - } - if (contrast < scanMinContrast_) - scanMinContrast_ = contrast; - scanData_.emplace_back(ScanRecord{ ftarget_, contrast, phase, conf }); - - if (scanState_ == ScanState::Coarse) { - if (ftarget_ >= cfg_.ranges[range_].focusMax || - contrast < cfg_.speeds[speed_].contrastRatio * scanMaxContrast_) { - /* - * Finished course scan, or termination based on contrast. - * Jump to just after max contrast and start fine scan. - */ - ftarget_ = std::min(ftarget_, findPeak(scanMaxIndex_) + - 2.0 * cfg_.speeds[speed_].stepFine); - scanState_ = ScanState::Fine; - scanData_.clear(); - } else - ftarget_ += cfg_.speeds[speed_].stepCoarse; - } else { /* ScanState::Fine */ - if (ftarget_ <= cfg_.ranges[range_].focusMin || scanData_.size() >= 5 || - contrast < cfg_.speeds[speed_].contrastRatio * scanMaxContrast_) { - /* - * Finished fine scan, or termination based on contrast. - * Use quadratic peak-finding to find best contrast position. - */ - ftarget_ = findPeak(scanMaxIndex_); - scanState_ = ScanState::Settle; - } else - ftarget_ -= cfg_.speeds[speed_].stepFine; - } - - stepCount_ = (ftarget_ == fsmooth_) ? 0 : cfg_.speeds[speed_].stepFrames; -} - -void Af::doAF(double contrast, double phase, double conf) -{ - /* Skip frames at startup and after sensor mode change */ - if (skipCount_ > 0) { - LOG(RPiAf, Debug) << "SKIP"; - skipCount_--; - return; - } - - if (scanState_ == ScanState::Pdaf) { - /* - * Use PDAF closed-loop control whenever available, in both CAF - * mode and (for a limited number of iterations) when triggered. - * If PDAF fails (due to poor contrast, noise or large defocus), - * fall back to a CDAF-based scan. To avoid "nuisance" scans, - * scan only after a number of frames with low PDAF confidence. - */ - if (conf > (dropCount_ ? 1.0 : 0.25) * cfg_.confEpsilon) { - doPDAF(phase, conf); - if (stepCount_ > 0) - stepCount_--; - else if (mode_ != AfModeContinuous) - scanState_ = ScanState::Idle; - dropCount_ = 0; - } else if (++dropCount_ == cfg_.speeds[speed_].dropoutFrames) - startProgrammedScan(); - } else if (scanState_ >= ScanState::Coarse && fsmooth_ == ftarget_) { - /* - * Scanning sequence. This means PDAF has become unavailable. - * Allow a delay between steps for CDAF FoM statistics to be - * updated, and a "settling time" at the end of the sequence. - * [A coarse or fine scan can be abandoned if two PDAF samples - * allow direct interpolation of the zero-phase lens position.] - */ - if (stepCount_ > 0) - stepCount_--; - else if (scanState_ == ScanState::Settle) { - if (prevContrast_ >= cfg_.speeds[speed_].contrastRatio * scanMaxContrast_ && - scanMinContrast_ <= cfg_.speeds[speed_].contrastRatio * scanMaxContrast_) - reportState_ = AfState::Focused; - else - reportState_ = AfState::Failed; - if (mode_ == AfModeContinuous && !pauseFlag_ && - cfg_.speeds[speed_].dropoutFrames > 0) - scanState_ = ScanState::Pdaf; - else - scanState_ = ScanState::Idle; - scanData_.clear(); - } else if (conf >= cfg_.confEpsilon && earlyTerminationByPhase(phase)) { - scanState_ = ScanState::Settle; - stepCount_ = (mode_ == AfModeContinuous) ? 0 - : cfg_.speeds[speed_].stepFrames; - } else - doScan(contrast, phase, conf); - } -} - -void Af::updateLensPosition() -{ - if (scanState_ >= ScanState::Pdaf) { - ftarget_ = std::clamp(ftarget_, - cfg_.ranges[range_].focusMin, - cfg_.ranges[range_].focusMax); - } - - if (initted_) { - /* from a known lens position: apply slew rate limit */ - fsmooth_ = std::clamp(ftarget_, - fsmooth_ - cfg_.speeds[speed_].maxSlew, - fsmooth_ + cfg_.speeds[speed_].maxSlew); - } else { - /* from an unknown position: go straight to target, but add delay */ - fsmooth_ = ftarget_; - initted_ = true; - skipCount_ = cfg_.skipFrames; - } -} - -void Af::startAF() -{ - /* Use PDAF if the tuning file allows it; else CDAF. */ - if (cfg_.speeds[speed_].dropoutFrames > 0 && - (mode_ == AfModeContinuous || cfg_.speeds[speed_].pdafFrames > 0)) { - if (!initted_) { - ftarget_ = cfg_.ranges[range_].focusDefault; - updateLensPosition(); - } - stepCount_ = (mode_ == AfModeContinuous) ? 0 : cfg_.speeds[speed_].pdafFrames; - scanState_ = ScanState::Pdaf; - scanData_.clear(); - dropCount_ = 0; - reportState_ = AfState::Scanning; - } else - startProgrammedScan(); -} - -void Af::startProgrammedScan() -{ - ftarget_ = cfg_.ranges[range_].focusMin; - updateLensPosition(); - scanState_ = ScanState::Coarse; - scanMaxContrast_ = 0.0; - scanMinContrast_ = 1.0e9; - scanMaxIndex_ = 0; - scanData_.clear(); - stepCount_ = cfg_.speeds[speed_].stepFrames; - reportState_ = AfState::Scanning; -} - -void Af::goIdle() -{ - scanState_ = ScanState::Idle; - reportState_ = AfState::Idle; - scanData_.clear(); -} - -/* - * PDAF phase data are available in prepare(), but CDAF statistics are not - * available until process(). We are gambling on the availability of PDAF. - * To expedite feedback control using PDAF, issue the V4L2 lens control from - * prepare(). Conversely, during scans, we must allow an extra frame delay - * between steps, to retrieve CDAF statistics from the previous process() - * so we can terminate the scan early without having to change our minds. - */ - -void Af::prepare(Metadata *imageMetadata) -{ - /* Initialize for triggered scan or start of CAF mode */ - if (scanState_ == ScanState::Trigger) - startAF(); - - if (initted_) { - /* Get PDAF from the embedded metadata, and run AF algorithm core */ - PdafRegions regions; - double phase = 0.0, conf = 0.0; - double oldFt = ftarget_; - double oldFs = fsmooth_; - ScanState oldSs = scanState_; - uint32_t oldSt = stepCount_; - if (imageMetadata->get("pdaf.regions", regions) == 0) - getPhase(regions, phase, conf); - doAF(prevContrast_, phase, conf); - updateLensPosition(); - LOG(RPiAf, Debug) << std::fixed << std::setprecision(2) - << static_cast(reportState_) - << " sst" << static_cast(oldSs) - << "->" << static_cast(scanState_) - << " stp" << oldSt << "->" << stepCount_ - << " ft" << oldFt << "->" << ftarget_ - << " fs" << oldFs << "->" << fsmooth_ - << " cont=" << (int)prevContrast_ - << " phase=" << (int)phase << " conf=" << (int)conf; - } - - /* Report status and produce new lens setting */ - AfStatus status; - if (pauseFlag_) - status.pauseState = (scanState_ == ScanState::Idle) ? AfPauseState::Paused - : AfPauseState::Pausing; - else - status.pauseState = AfPauseState::Running; - - if (mode_ == AfModeAuto && scanState_ != ScanState::Idle) - status.state = AfState::Scanning; - else - status.state = reportState_; - status.lensSetting = initted_ ? std::optional(cfg_.map.eval(fsmooth_)) - : std::nullopt; - imageMetadata->set("af.status", status); -} - -void Af::process(StatisticsPtr &stats, [[maybe_unused]] Metadata *imageMetadata) -{ - (void)imageMetadata; - prevContrast_ = getContrast(stats->focusRegions); -} - -/* Controls */ - -void Af::setRange(AfRange r) -{ - LOG(RPiAf, Debug) << "setRange: " << (unsigned)r; - if (r < AfAlgorithm::AfRangeMax) - range_ = r; -} - -void Af::setSpeed(AfSpeed s) -{ - LOG(RPiAf, Debug) << "setSpeed: " << (unsigned)s; - if (s < AfAlgorithm::AfSpeedMax) { - if (scanState_ == ScanState::Pdaf && - cfg_.speeds[s].pdafFrames > cfg_.speeds[speed_].pdafFrames) - stepCount_ += cfg_.speeds[s].pdafFrames - cfg_.speeds[speed_].pdafFrames; - speed_ = s; - } -} - -void Af::setMetering(bool mode) -{ - if (useWindows_ != mode) { - useWindows_ = mode; - invalidateWeights(); - } -} - -void Af::setWindows(libcamera::Span const &wins) -{ - windows_.clear(); - for (auto &w : wins) { - LOG(RPiAf, Debug) << "Window: " - << w.x << ", " - << w.y << ", " - << w.width << ", " - << w.height; - windows_.push_back(w); - if (windows_.size() >= MaxWindows) - break; - } - - if (useWindows_) - invalidateWeights(); -} - -bool Af::setLensPosition(double dioptres, int *hwpos) -{ - bool changed = false; - - if (mode_ == AfModeManual) { - LOG(RPiAf, Debug) << "setLensPosition: " << dioptres; - ftarget_ = cfg_.map.domain().clip(dioptres); - changed = !(initted_ && fsmooth_ == ftarget_); - updateLensPosition(); - } - - if (hwpos) - *hwpos = cfg_.map.eval(fsmooth_); - - return changed; -} - -std::optional Af::getLensPosition() const -{ - /* - * \todo We ought to perform some precise timing here to determine - * the current lens position. - */ - return initted_ ? std::optional(fsmooth_) : std::nullopt; -} - -void Af::cancelScan() -{ - LOG(RPiAf, Debug) << "cancelScan"; - if (mode_ == AfModeAuto) - goIdle(); -} - -void Af::triggerScan() -{ - LOG(RPiAf, Debug) << "triggerScan"; - if (mode_ == AfModeAuto && scanState_ == ScanState::Idle) - scanState_ = ScanState::Trigger; -} - -void Af::setMode(AfAlgorithm::AfMode mode) -{ - LOG(RPiAf, Debug) << "setMode: " << (unsigned)mode; - if (mode_ != mode) { - mode_ = mode; - pauseFlag_ = false; - if (mode == AfModeContinuous) - scanState_ = ScanState::Trigger; - else if (mode != AfModeAuto || scanState_ < ScanState::Coarse) - goIdle(); - } -} - -AfAlgorithm::AfMode Af::getMode() const -{ - return mode_; -} - -void Af::pause(AfAlgorithm::AfPause pause) -{ - LOG(RPiAf, Debug) << "pause: " << (unsigned)pause; - if (mode_ == AfModeContinuous) { - if (pause == AfPauseResume && pauseFlag_) { - pauseFlag_ = false; - if (scanState_ < ScanState::Coarse) - scanState_ = ScanState::Trigger; - } else if (pause != AfPauseResume && !pauseFlag_) { - pauseFlag_ = true; - if (pause == AfPauseImmediate || scanState_ < ScanState::Coarse) - goIdle(); - } - } -} - -// Register algorithm with the system. -static Algorithm *create(Controller *controller) -{ - return (Algorithm *)new Af(controller); -} -static RegisterAlgorithm reg(NAME, &create); diff --git a/src/ipa/raspberrypi/controller/rpi/af.h b/src/ipa/raspberrypi/controller/rpi/af.h deleted file mode 100644 index 6d2bae67..00000000 --- a/src/ipa/raspberrypi/controller/rpi/af.h +++ /dev/null @@ -1,165 +0,0 @@ -/* SPDX-License-Identifier: BSD-2-Clause */ -/* - * Copyright (C) 2022-2023, Raspberry Pi Ltd - * - * af.h - Autofocus control algorithm - */ -#pragma once - -#include "../af_algorithm.h" -#include "../af_status.h" -#include "../pdaf_data.h" -#include "../pwl.h" - -/* - * This algorithm implements a hybrid of CDAF and PDAF, favouring PDAF. - * - * Whenever PDAF is available, it is used in a continuous feedback loop. - * When triggered in auto mode, we simply enable AF for a limited number - * of frames (it may terminate early if the delta becomes small enough). - * - * When PDAF confidence is low (due e.g. to low contrast or extreme defocus) - * or PDAF data are absent, fall back to CDAF with a programmed scan pattern. - * A coarse and fine scan are performed, using ISP's CDAF focus FoM to - * estimate the lens position with peak contrast. This is slower due to - * extra latency in the ISP, and requires a settling time between steps. - * - * Some hysteresis is applied to the switch between PDAF and CDAF, to avoid - * "nuisance" scans. During each interval where PDAF is not working, only - * ONE scan will be performed; CAF cannot track objects using CDAF alone. - * - */ - -namespace RPiController { - -class Af : public AfAlgorithm -{ -public: - Af(Controller *controller = NULL); - ~Af(); - char const *name() const override; - int read(const libcamera::YamlObject ¶ms) override; - void initialise() override; - - /* IPA calls */ - void switchMode(CameraMode const &cameraMode, Metadata *metadata) override; - void prepare(Metadata *imageMetadata) override; - void process(StatisticsPtr &stats, Metadata *imageMetadata) override; - - /* controls */ - void setRange(AfRange range) override; - void setSpeed(AfSpeed speed) override; - void setMetering(bool use_windows) override; - void setWindows(libcamera::Span const &wins) override; - void setMode(AfMode mode) override; - AfMode getMode() const override; - bool setLensPosition(double dioptres, int32_t *hwpos) override; - std::optional getLensPosition() const override; - void triggerScan() override; - void cancelScan() override; - void pause(AfPause pause) override; - -private: - enum class ScanState { - Idle = 0, - Trigger, - Pdaf, - Coarse, - Fine, - Settle - }; - - struct RangeDependentParams { - double focusMin; /* lower (far) limit in dipotres */ - double focusMax; /* upper (near) limit in dioptres */ - double focusDefault; /* default setting ("hyperfocal") */ - - RangeDependentParams(); - void read(const libcamera::YamlObject ¶ms); - }; - - struct SpeedDependentParams { - double stepCoarse; /* used for scans */ - double stepFine; /* used for scans */ - double contrastRatio; /* used for scan termination and reporting */ - double pdafGain; /* coefficient for PDAF feedback loop */ - double pdafSquelch; /* PDAF stability parameter (device-specific) */ - double maxSlew; /* limit for lens movement per frame */ - uint32_t pdafFrames; /* number of iterations when triggered */ - uint32_t dropoutFrames; /* number of non-PDAF frames to switch to CDAF */ - uint32_t stepFrames; /* frames to skip in between steps of a scan */ - - SpeedDependentParams(); - void read(const libcamera::YamlObject ¶ms); - }; - - struct CfgParams { - RangeDependentParams ranges[AfRangeMax]; - SpeedDependentParams speeds[AfSpeedMax]; - uint32_t confEpsilon; /* PDAF hysteresis threshold (sensor-specific) */ - uint32_t confThresh; /* PDAF confidence cell min (sensor-specific) */ - uint32_t confClip; /* PDAF confidence cell max (sensor-specific) */ - uint32_t skipFrames; /* frames to skip at start or modeswitch */ - Pwl map; /* converts dioptres -> lens driver position */ - - CfgParams(); - int read(const libcamera::YamlObject ¶ms); - void initialise(); - }; - - struct ScanRecord { - double focus; - double contrast; - double phase; - double conf; - }; - - struct RegionWeights { - unsigned rows; - unsigned cols; - uint32_t sum; - std::vector w; - - RegionWeights() - : rows(0), cols(0), sum(0), w() {} - }; - - void computeWeights(RegionWeights *wgts, unsigned rows, unsigned cols); - void invalidateWeights(); - bool getPhase(PdafRegions const ®ions, double &phase, double &conf); - double getContrast(const FocusRegions &focusStats); - void doPDAF(double phase, double conf); - bool earlyTerminationByPhase(double phase); - double findPeak(unsigned index) const; - void doScan(double contrast, double phase, double conf); - void doAF(double contrast, double phase, double conf); - void updateLensPosition(); - void startAF(); - void startProgrammedScan(); - void goIdle(); - - /* Configuration and settings */ - CfgParams cfg_; - AfRange range_; - AfSpeed speed_; - AfMode mode_; - bool pauseFlag_; - libcamera::Rectangle statsRegion_; - std::vector windows_; - bool useWindows_; - RegionWeights phaseWeights_; - RegionWeights contrastWeights_; - - /* Working state. */ - ScanState scanState_; - bool initted_; - double ftarget_, fsmooth_; - double prevContrast_; - unsigned skipCount_, stepCount_, dropCount_; - unsigned scanMaxIndex_; - double scanMaxContrast_, scanMinContrast_; - std::vector scanData_; - AfState reportState_; -}; - -} // namespace RPiController diff --git a/src/ipa/raspberrypi/controller/rpi/agc.cpp b/src/ipa/raspberrypi/controller/rpi/agc.cpp deleted file mode 100644 index e6fb7b8d..00000000 --- a/src/ipa/raspberrypi/controller/rpi/agc.cpp +++ /dev/null @@ -1,922 +0,0 @@ -/* SPDX-License-Identifier: BSD-2-Clause */ -/* - * Copyright (C) 2019, Raspberry Pi Ltd - * - * agc.cpp - AGC/AEC control algorithm - */ - -#include -#include -#include - -#include - -#include "../awb_status.h" -#include "../device_status.h" -#include "../histogram.h" -#include "../lux_status.h" -#include "../metadata.h" - -#include "agc.h" - -using namespace RPiController; -using namespace libcamera; -using libcamera::utils::Duration; -using namespace std::literals::chrono_literals; - -LOG_DEFINE_CATEGORY(RPiAgc) - -#define NAME "rpi.agc" - -int AgcMeteringMode::read(const libcamera::YamlObject ¶ms) -{ - const YamlObject &yamlWeights = params["weights"]; - - for (const auto &p : yamlWeights.asList()) { - auto value = p.get(); - if (!value) - return -EINVAL; - weights.push_back(*value); - } - - return 0; -} - -static std::tuple -readMeteringModes(std::map &metering_modes, - const libcamera::YamlObject ¶ms) -{ - std::string first; - int ret; - - for (const auto &[key, value] : params.asDict()) { - AgcMeteringMode meteringMode; - ret = meteringMode.read(value); - if (ret) - return { ret, {} }; - - metering_modes[key] = std::move(meteringMode); - if (first.empty()) - first = key; - } - - return { 0, first }; -} - -int AgcExposureMode::read(const libcamera::YamlObject ¶ms) -{ - auto value = params["shutter"].getList(); - if (!value) - return -EINVAL; - std::transform(value->begin(), value->end(), std::back_inserter(shutter), - [](double v) { return v * 1us; }); - - value = params["gain"].getList(); - if (!value) - return -EINVAL; - gain = std::move(*value); - - if (shutter.size() < 2 || gain.size() < 2) { - LOG(RPiAgc, Error) - << "AgcExposureMode: must have at least two entries in exposure profile"; - return -EINVAL; - } - - if (shutter.size() != gain.size()) { - LOG(RPiAgc, Error) - << "AgcExposureMode: expect same number of exposure and gain entries in exposure profile"; - return -EINVAL; - } - - return 0; -} - -static std::tuple -readExposureModes(std::map &exposureModes, - const libcamera::YamlObject ¶ms) -{ - std::string first; - int ret; - - for (const auto &[key, value] : params.asDict()) { - AgcExposureMode exposureMode; - ret = exposureMode.read(value); - if (ret) - return { ret, {} }; - - exposureModes[key] = std::move(exposureMode); - if (first.empty()) - first = key; - } - - return { 0, first }; -} - -int AgcConstraint::read(const libcamera::YamlObject ¶ms) -{ - std::string boundString = params["bound"].get(""); - transform(boundString.begin(), boundString.end(), - boundString.begin(), ::toupper); - if (boundString != "UPPER" && boundString != "LOWER") { - LOG(RPiAgc, Error) << "AGC constraint type should be UPPER or LOWER"; - return -EINVAL; - } - bound = boundString == "UPPER" ? Bound::UPPER : Bound::LOWER; - - auto value = params["q_lo"].get(); - if (!value) - return -EINVAL; - qLo = *value; - - value = params["q_hi"].get(); - if (!value) - return -EINVAL; - qHi = *value; - - return yTarget.read(params["y_target"]); -} - -static std::tuple -readConstraintMode(const libcamera::YamlObject ¶ms) -{ - AgcConstraintMode mode; - int ret; - - for (const auto &p : params.asList()) { - AgcConstraint constraint; - ret = constraint.read(p); - if (ret) - return { ret, {} }; - - mode.push_back(std::move(constraint)); - } - - return { 0, mode }; -} - -static std::tuple -readConstraintModes(std::map &constraintModes, - const libcamera::YamlObject ¶ms) -{ - std::string first; - int ret; - - for (const auto &[key, value] : params.asDict()) { - std::tie(ret, constraintModes[key]) = readConstraintMode(value); - if (ret) - return { ret, {} }; - - if (first.empty()) - first = key; - } - - return { 0, first }; -} - -int AgcConfig::read(const libcamera::YamlObject ¶ms) -{ - LOG(RPiAgc, Debug) << "AgcConfig"; - int ret; - - std::tie(ret, defaultMeteringMode) = - readMeteringModes(meteringModes, params["metering_modes"]); - if (ret) - return ret; - std::tie(ret, defaultExposureMode) = - readExposureModes(exposureModes, params["exposure_modes"]); - if (ret) - return ret; - std::tie(ret, defaultConstraintMode) = - readConstraintModes(constraintModes, params["constraint_modes"]); - if (ret) - return ret; - - ret = yTarget.read(params["y_target"]); - if (ret) - return ret; - - speed = params["speed"].get(0.2); - startupFrames = params["startup_frames"].get(10); - convergenceFrames = params["convergence_frames"].get(6); - fastReduceThreshold = params["fast_reduce_threshold"].get(0.4); - baseEv = params["base_ev"].get(1.0); - - /* Start with quite a low value as ramping up is easier than ramping down. */ - defaultExposureTime = params["default_exposure_time"].get(1000) * 1us; - defaultAnalogueGain = params["default_analogue_gain"].get(1.0); - - return 0; -} - -Agc::ExposureValues::ExposureValues() - : shutter(0s), analogueGain(0), - totalExposure(0s), totalExposureNoDG(0s) -{ -} - -Agc::Agc(Controller *controller) - : AgcAlgorithm(controller), meteringMode_(nullptr), - exposureMode_(nullptr), constraintMode_(nullptr), - frameCount_(0), lockCount_(0), - lastTargetExposure_(0s), ev_(1.0), flickerPeriod_(0s), - maxShutter_(0s), fixedShutter_(0s), fixedAnalogueGain_(0.0) -{ - memset(&awb_, 0, sizeof(awb_)); - /* - * Setting status_.totalExposureValue_ to zero initially tells us - * it's not been calculated yet (i.e. Process hasn't yet run). - */ - memset(&status_, 0, sizeof(status_)); - status_.ev = ev_; -} - -char const *Agc::name() const -{ - return NAME; -} - -int Agc::read(const libcamera::YamlObject ¶ms) -{ - LOG(RPiAgc, Debug) << "Agc"; - - int ret = config_.read(params); - if (ret) - return ret; - - const Size &size = getHardwareConfig().agcZoneWeights; - for (auto const &modes : config_.meteringModes) { - if (modes.second.weights.size() != size.width * size.height) { - LOG(RPiAgc, Error) << "AgcMeteringMode: Incorrect number of weights"; - return -EINVAL; - } - } - - /* - * Set the config's defaults (which are the first ones it read) as our - * current modes, until someone changes them. (they're all known to - * exist at this point) - */ - meteringModeName_ = config_.defaultMeteringMode; - meteringMode_ = &config_.meteringModes[meteringModeName_]; - exposureModeName_ = config_.defaultExposureMode; - exposureMode_ = &config_.exposureModes[exposureModeName_]; - constraintModeName_ = config_.defaultConstraintMode; - constraintMode_ = &config_.constraintModes[constraintModeName_]; - /* Set up the "last shutter/gain" values, in case AGC starts "disabled". */ - status_.shutterTime = config_.defaultExposureTime; - status_.analogueGain = config_.defaultAnalogueGain; - return 0; -} - -void Agc::disableAuto() -{ - fixedShutter_ = status_.shutterTime; - fixedAnalogueGain_ = status_.analogueGain; -} - -void Agc::enableAuto() -{ - fixedShutter_ = 0s; - fixedAnalogueGain_ = 0; -} - -unsigned int Agc::getConvergenceFrames() const -{ - /* - * If shutter and gain have been explicitly set, there is no - * convergence to happen, so no need to drop any frames - return zero. - */ - if (fixedShutter_ && fixedAnalogueGain_) - return 0; - else - return config_.convergenceFrames; -} - -void Agc::setEv(double ev) -{ - ev_ = ev; -} - -void Agc::setFlickerPeriod(Duration flickerPeriod) -{ - flickerPeriod_ = flickerPeriod; -} - -void Agc::setMaxShutter(Duration maxShutter) -{ - maxShutter_ = maxShutter; -} - -void Agc::setFixedShutter(Duration fixedShutter) -{ - fixedShutter_ = fixedShutter; - /* Set this in case someone calls disableAuto() straight after. */ - status_.shutterTime = limitShutter(fixedShutter_); -} - -void Agc::setFixedAnalogueGain(double fixedAnalogueGain) -{ - fixedAnalogueGain_ = fixedAnalogueGain; - /* Set this in case someone calls disableAuto() straight after. */ - status_.analogueGain = limitGain(fixedAnalogueGain); -} - -void Agc::setMeteringMode(std::string const &meteringModeName) -{ - meteringModeName_ = meteringModeName; -} - -void Agc::setExposureMode(std::string const &exposureModeName) -{ - exposureModeName_ = exposureModeName; -} - -void Agc::setConstraintMode(std::string const &constraintModeName) -{ - constraintModeName_ = constraintModeName; -} - -void Agc::switchMode(CameraMode const &cameraMode, - Metadata *metadata) -{ - /* AGC expects the mode sensitivity always to be non-zero. */ - ASSERT(cameraMode.sensitivity); - - housekeepConfig(); - - /* - * Store the mode in the local state. We must cache the sensitivity of - * of the previous mode for the calculations below. - */ - double lastSensitivity = mode_.sensitivity; - mode_ = cameraMode; - - Duration fixedShutter = limitShutter(fixedShutter_); - if (fixedShutter && fixedAnalogueGain_) { - /* We're going to reset the algorithm here with these fixed values. */ - - fetchAwbStatus(metadata); - double minColourGain = std::min({ awb_.gainR, awb_.gainG, awb_.gainB, 1.0 }); - ASSERT(minColourGain != 0.0); - - /* This is the equivalent of computeTargetExposure and applyDigitalGain. */ - target_.totalExposureNoDG = fixedShutter_ * fixedAnalogueGain_; - target_.totalExposure = target_.totalExposureNoDG / minColourGain; - - /* Equivalent of filterExposure. This resets any "history". */ - filtered_ = target_; - - /* Equivalent of divideUpExposure. */ - filtered_.shutter = fixedShutter; - filtered_.analogueGain = fixedAnalogueGain_; - } else if (status_.totalExposureValue) { - /* - * On a mode switch, various things could happen: - * - the exposure profile might change - * - a fixed exposure or gain might be set - * - the new mode's sensitivity might be different - * We cope with the last of these by scaling the target values. After - * that we just need to re-divide the exposure/gain according to the - * current exposure profile, which takes care of everything else. - */ - - double ratio = lastSensitivity / cameraMode.sensitivity; - target_.totalExposureNoDG *= ratio; - target_.totalExposure *= ratio; - filtered_.totalExposureNoDG *= ratio; - filtered_.totalExposure *= ratio; - - divideUpExposure(); - } else { - /* - * We come through here on startup, when at least one of the shutter - * or gain has not been fixed. We must still write those values out so - * that they will be applied immediately. We supply some arbitrary defaults - * for any that weren't set. - */ - - /* Equivalent of divideUpExposure. */ - filtered_.shutter = fixedShutter ? fixedShutter : config_.defaultExposureTime; - filtered_.analogueGain = fixedAnalogueGain_ ? fixedAnalogueGain_ : config_.defaultAnalogueGain; - } - - writeAndFinish(metadata, false); -} - -void Agc::prepare(Metadata *imageMetadata) -{ - Duration totalExposureValue = status_.totalExposureValue; - AgcStatus delayedStatus; - - if (!imageMetadata->get("agc.delayed_status", delayedStatus)) - totalExposureValue = delayedStatus.totalExposureValue; - - status_.digitalGain = 1.0; - fetchAwbStatus(imageMetadata); /* always fetch it so that Process knows it's been done */ - - if (status_.totalExposureValue) { - /* Process has run, so we have meaningful values. */ - DeviceStatus deviceStatus; - if (imageMetadata->get("device.status", deviceStatus) == 0) { - Duration actualExposure = deviceStatus.shutterSpeed * - deviceStatus.analogueGain; - if (actualExposure) { - status_.digitalGain = totalExposureValue / actualExposure; - LOG(RPiAgc, Debug) << "Want total exposure " << totalExposureValue; - /* - * Never ask for a gain < 1.0, and also impose - * some upper limit. Make it customisable? - */ - status_.digitalGain = std::max(1.0, std::min(status_.digitalGain, 4.0)); - LOG(RPiAgc, Debug) << "Actual exposure " << actualExposure; - LOG(RPiAgc, Debug) << "Use digitalGain " << status_.digitalGain; - LOG(RPiAgc, Debug) << "Effective exposure " - << actualExposure * status_.digitalGain; - /* Decide whether AEC/AGC has converged. */ - updateLockStatus(deviceStatus); - } - } else - LOG(RPiAgc, Warning) << name() << ": no device metadata"; - imageMetadata->set("agc.status", status_); - } -} - -void Agc::process(StatisticsPtr &stats, Metadata *imageMetadata) -{ - frameCount_++; - /* - * First a little bit of housekeeping, fetching up-to-date settings and - * configuration, that kind of thing. - */ - housekeepConfig(); - /* Get the current exposure values for the frame that's just arrived. */ - fetchCurrentExposure(imageMetadata); - /* Compute the total gain we require relative to the current exposure. */ - double gain, targetY; - computeGain(stats, imageMetadata, gain, targetY); - /* Now compute the target (final) exposure which we think we want. */ - computeTargetExposure(gain); - /* - * Some of the exposure has to be applied as digital gain, so work out - * what that is. This function also tells us whether it's decided to - * "desaturate" the image more quickly. - */ - bool desaturate = applyDigitalGain(gain, targetY); - /* The results have to be filtered so as not to change too rapidly. */ - filterExposure(desaturate); - /* - * The last thing is to divide up the exposure value into a shutter time - * and analogue gain, according to the current exposure mode. - */ - divideUpExposure(); - /* Finally advertise what we've done. */ - writeAndFinish(imageMetadata, desaturate); -} - -void Agc::updateLockStatus(DeviceStatus const &deviceStatus) -{ - const double errorFactor = 0.10; /* make these customisable? */ - const int maxLockCount = 5; - /* Reset "lock count" when we exceed this multiple of errorFactor */ - const double resetMargin = 1.5; - - /* Add 200us to the exposure time error to allow for line quantisation. */ - Duration exposureError = lastDeviceStatus_.shutterSpeed * errorFactor + 200us; - double gainError = lastDeviceStatus_.analogueGain * errorFactor; - Duration targetError = lastTargetExposure_ * errorFactor; - - /* - * Note that we don't know the exposure/gain limits of the sensor, so - * the values we keep requesting may be unachievable. For this reason - * we only insist that we're close to values in the past few frames. - */ - if (deviceStatus.shutterSpeed > lastDeviceStatus_.shutterSpeed - exposureError && - deviceStatus.shutterSpeed < lastDeviceStatus_.shutterSpeed + exposureError && - deviceStatus.analogueGain > lastDeviceStatus_.analogueGain - gainError && - deviceStatus.analogueGain < lastDeviceStatus_.analogueGain + gainError && - status_.targetExposureValue > lastTargetExposure_ - targetError && - status_.targetExposureValue < lastTargetExposure_ + targetError) - lockCount_ = std::min(lockCount_ + 1, maxLockCount); - else if (deviceStatus.shutterSpeed < lastDeviceStatus_.shutterSpeed - resetMargin * exposureError || - deviceStatus.shutterSpeed > lastDeviceStatus_.shutterSpeed + resetMargin * exposureError || - deviceStatus.analogueGain < lastDeviceStatus_.analogueGain - resetMargin * gainError || - deviceStatus.analogueGain > lastDeviceStatus_.analogueGain + resetMargin * gainError || - status_.targetExposureValue < lastTargetExposure_ - resetMargin * targetError || - status_.targetExposureValue > lastTargetExposure_ + resetMargin * targetError) - lockCount_ = 0; - - lastDeviceStatus_ = deviceStatus; - lastTargetExposure_ = status_.targetExposureValue; - - LOG(RPiAgc, Debug) << "Lock count updated to " << lockCount_; - status_.locked = lockCount_ == maxLockCount; -} - -static void copyString(std::string const &s, char *d, size_t size) -{ - size_t length = s.copy(d, size - 1); - d[length] = '\0'; -} - -void Agc::housekeepConfig() -{ - /* First fetch all the up-to-date settings, so no one else has to do it. */ - status_.ev = ev_; - status_.fixedShutter = limitShutter(fixedShutter_); - status_.fixedAnalogueGain = fixedAnalogueGain_; - status_.flickerPeriod = flickerPeriod_; - LOG(RPiAgc, Debug) << "ev " << status_.ev << " fixedShutter " - << status_.fixedShutter << " fixedAnalogueGain " - << status_.fixedAnalogueGain; - /* - * Make sure the "mode" pointers point to the up-to-date things, if - * they've changed. - */ - if (strcmp(meteringModeName_.c_str(), status_.meteringMode)) { - auto it = config_.meteringModes.find(meteringModeName_); - if (it == config_.meteringModes.end()) - LOG(RPiAgc, Fatal) << "No metering mode " << meteringModeName_; - meteringMode_ = &it->second; - copyString(meteringModeName_, status_.meteringMode, - sizeof(status_.meteringMode)); - } - if (strcmp(exposureModeName_.c_str(), status_.exposureMode)) { - auto it = config_.exposureModes.find(exposureModeName_); - if (it == config_.exposureModes.end()) - LOG(RPiAgc, Fatal) << "No exposure profile " << exposureModeName_; - exposureMode_ = &it->second; - copyString(exposureModeName_, status_.exposureMode, - sizeof(status_.exposureMode)); - } - if (strcmp(constraintModeName_.c_str(), status_.constraintMode)) { - auto it = - config_.constraintModes.find(constraintModeName_); - if (it == config_.constraintModes.end()) - LOG(RPiAgc, Fatal) << "No constraint list " << constraintModeName_; - constraintMode_ = &it->second; - copyString(constraintModeName_, status_.constraintMode, - sizeof(status_.constraintMode)); - } - LOG(RPiAgc, Debug) << "exposureMode " - << exposureModeName_ << " constraintMode " - << constraintModeName_ << " meteringMode " - << meteringModeName_; -} - -void Agc::fetchCurrentExposure(Metadata *imageMetadata) -{ - std::unique_lock lock(*imageMetadata); - DeviceStatus *deviceStatus = - imageMetadata->getLocked("device.status"); - if (!deviceStatus) - LOG(RPiAgc, Fatal) << "No device metadata"; - current_.shutter = deviceStatus->shutterSpeed; - current_.analogueGain = deviceStatus->analogueGain; - AgcStatus *agcStatus = - imageMetadata->getLocked("agc.status"); - current_.totalExposure = agcStatus ? agcStatus->totalExposureValue : 0s; - current_.totalExposureNoDG = current_.shutter * current_.analogueGain; -} - -void Agc::fetchAwbStatus(Metadata *imageMetadata) -{ - awb_.gainR = 1.0; /* in case not found in metadata */ - awb_.gainG = 1.0; - awb_.gainB = 1.0; - if (imageMetadata->get("awb.status", awb_) != 0) - LOG(RPiAgc, Debug) << "No AWB status found"; -} - -static double computeInitialY(StatisticsPtr &stats, AwbStatus const &awb, - std::vector &weights, double gain) -{ - constexpr uint64_t maxVal = 1 << Statistics::NormalisationFactorPow2; - - ASSERT(weights.size() == stats->agcRegions.numRegions()); - - /* - * Note how the calculation below means that equal weights give you - * "average" metering (i.e. all pixels equally important). - */ - double rSum = 0, gSum = 0, bSum = 0, pixelSum = 0; - for (unsigned int i = 0; i < stats->agcRegions.numRegions(); i++) { - auto ®ion = stats->agcRegions.get(i); - double rAcc = std::min(region.val.rSum * gain, (maxVal - 1) * region.counted); - double gAcc = std::min(region.val.gSum * gain, (maxVal - 1) * region.counted); - double bAcc = std::min(region.val.bSum * gain, (maxVal - 1) * region.counted); - rSum += rAcc * weights[i]; - gSum += gAcc * weights[i]; - bSum += bAcc * weights[i]; - pixelSum += region.counted * weights[i]; - } - if (pixelSum == 0.0) { - LOG(RPiAgc, Warning) << "computeInitialY: pixelSum is zero"; - return 0; - } - double ySum = rSum * awb.gainR * .299 + - gSum * awb.gainG * .587 + - bSum * awb.gainB * .114; - return ySum / pixelSum / maxVal; -} - -/* - * We handle extra gain through EV by adjusting our Y targets. However, you - * simply can't monitor histograms once they get very close to (or beyond!) - * saturation, so we clamp the Y targets to this value. It does mean that EV - * increases don't necessarily do quite what you might expect in certain - * (contrived) cases. - */ - -static constexpr double EvGainYTargetLimit = 0.9; - -static double constraintComputeGain(AgcConstraint &c, const Histogram &h, double lux, - double evGain, double &targetY) -{ - targetY = c.yTarget.eval(c.yTarget.domain().clip(lux)); - targetY = std::min(EvGainYTargetLimit, targetY * evGain); - double iqm = h.interQuantileMean(c.qLo, c.qHi); - return (targetY * h.bins()) / iqm; -} - -void Agc::computeGain(StatisticsPtr &statistics, Metadata *imageMetadata, - double &gain, double &targetY) -{ - struct LuxStatus lux = {}; - lux.lux = 400; /* default lux level to 400 in case no metadata found */ - if (imageMetadata->get("lux.status", lux) != 0) - LOG(RPiAgc, Warning) << "No lux level found"; - const Histogram &h = statistics->yHist; - double evGain = status_.ev * config_.baseEv; - /* - * The initial gain and target_Y come from some of the regions. After - * that we consider the histogram constraints. - */ - targetY = config_.yTarget.eval(config_.yTarget.domain().clip(lux.lux)); - targetY = std::min(EvGainYTargetLimit, targetY * evGain); - - /* - * Do this calculation a few times as brightness increase can be - * non-linear when there are saturated regions. - */ - gain = 1.0; - for (int i = 0; i < 8; i++) { - double initialY = computeInitialY(statistics, awb_, meteringMode_->weights, gain); - double extraGain = std::min(10.0, targetY / (initialY + .001)); - gain *= extraGain; - LOG(RPiAgc, Debug) << "Initial Y " << initialY << " target " << targetY - << " gives gain " << gain; - if (extraGain < 1.01) /* close enough */ - break; - } - - for (auto &c : *constraintMode_) { - double newTargetY; - double newGain = constraintComputeGain(c, h, lux.lux, evGain, newTargetY); - LOG(RPiAgc, Debug) << "Constraint has target_Y " - << newTargetY << " giving gain " << newGain; - if (c.bound == AgcConstraint::Bound::LOWER && newGain > gain) { - LOG(RPiAgc, Debug) << "Lower bound constraint adopted"; - gain = newGain; - targetY = newTargetY; - } else if (c.bound == AgcConstraint::Bound::UPPER && newGain < gain) { - LOG(RPiAgc, Debug) << "Upper bound constraint adopted"; - gain = newGain; - targetY = newTargetY; - } - } - LOG(RPiAgc, Debug) << "Final gain " << gain << " (target_Y " << targetY << " ev " - << status_.ev << " base_ev " << config_.baseEv - << ")"; -} - -void Agc::computeTargetExposure(double gain) -{ - if (status_.fixedShutter && status_.fixedAnalogueGain) { - /* - * When ag and shutter are both fixed, we need to drive the - * total exposure so that we end up with a digital gain of at least - * 1/minColourGain. Otherwise we'd desaturate channels causing - * white to go cyan or magenta. - */ - double minColourGain = std::min({ awb_.gainR, awb_.gainG, awb_.gainB, 1.0 }); - ASSERT(minColourGain != 0.0); - target_.totalExposure = - status_.fixedShutter * status_.fixedAnalogueGain / minColourGain; - } else { - /* - * The statistics reflect the image without digital gain, so the final - * total exposure we're aiming for is: - */ - target_.totalExposure = current_.totalExposureNoDG * gain; - /* The final target exposure is also limited to what the exposure mode allows. */ - Duration maxShutter = status_.fixedShutter - ? status_.fixedShutter - : exposureMode_->shutter.back(); - maxShutter = limitShutter(maxShutter); - Duration maxTotalExposure = - maxShutter * - (status_.fixedAnalogueGain != 0.0 - ? status_.fixedAnalogueGain - : exposureMode_->gain.back()); - target_.totalExposure = std::min(target_.totalExposure, maxTotalExposure); - } - LOG(RPiAgc, Debug) << "Target totalExposure " << target_.totalExposure; -} - -bool Agc::applyDigitalGain(double gain, double targetY) -{ - double minColourGain = std::min({ awb_.gainR, awb_.gainG, awb_.gainB, 1.0 }); - ASSERT(minColourGain != 0.0); - double dg = 1.0 / minColourGain; - /* - * I think this pipeline subtracts black level and rescales before we - * get the stats, so no need to worry about it. - */ - LOG(RPiAgc, Debug) << "after AWB, target dg " << dg << " gain " << gain - << " target_Y " << targetY; - /* - * Finally, if we're trying to reduce exposure but the target_Y is - * "close" to 1.0, then the gain computed for that constraint will be - * only slightly less than one, because the measured Y can never be - * larger than 1.0. When this happens, demand a large digital gain so - * that the exposure can be reduced, de-saturating the image much more - * quickly (and we then approach the correct value more quickly from - * below). - */ - bool desaturate = targetY > config_.fastReduceThreshold && - gain < sqrt(targetY); - if (desaturate) - dg /= config_.fastReduceThreshold; - LOG(RPiAgc, Debug) << "Digital gain " << dg << " desaturate? " << desaturate; - target_.totalExposureNoDG = target_.totalExposure / dg; - LOG(RPiAgc, Debug) << "Target totalExposureNoDG " << target_.totalExposureNoDG; - return desaturate; -} - -void Agc::filterExposure(bool desaturate) -{ - double speed = config_.speed; - /* - * AGC adapts instantly if both shutter and gain are directly specified - * or we're in the startup phase. - */ - if ((status_.fixedShutter && status_.fixedAnalogueGain) || - frameCount_ <= config_.startupFrames) - speed = 1.0; - if (!filtered_.totalExposure) { - filtered_.totalExposure = target_.totalExposure; - filtered_.totalExposureNoDG = target_.totalExposureNoDG; - } else { - /* - * If close to the result go faster, to save making so many - * micro-adjustments on the way. (Make this customisable?) - */ - if (filtered_.totalExposure < 1.2 * target_.totalExposure && - filtered_.totalExposure > 0.8 * target_.totalExposure) - speed = sqrt(speed); - filtered_.totalExposure = speed * target_.totalExposure + - filtered_.totalExposure * (1.0 - speed); - /* - * When desaturing, take a big jump down in totalExposureNoDG, - * which we'll hide with digital gain. - */ - if (desaturate) - filtered_.totalExposureNoDG = - target_.totalExposureNoDG; - else - filtered_.totalExposureNoDG = - speed * target_.totalExposureNoDG + - filtered_.totalExposureNoDG * (1.0 - speed); - } - /* - * We can't let the totalExposureNoDG exposure deviate too far below the - * total exposure, as there might not be enough digital gain available - * in the ISP to hide it (which will cause nasty oscillation). - */ - if (filtered_.totalExposureNoDG < - filtered_.totalExposure * config_.fastReduceThreshold) - filtered_.totalExposureNoDG = filtered_.totalExposure * config_.fastReduceThreshold; - LOG(RPiAgc, Debug) << "After filtering, totalExposure " << filtered_.totalExposure - << " no dg " << filtered_.totalExposureNoDG; -} - -void Agc::divideUpExposure() -{ - /* - * Sending the fixed shutter/gain cases through the same code may seem - * unnecessary, but it will make more sense when extend this to cover - * variable aperture. - */ - Duration exposureValue = filtered_.totalExposureNoDG; - Duration shutterTime; - double analogueGain; - shutterTime = status_.fixedShutter ? status_.fixedShutter - : exposureMode_->shutter[0]; - shutterTime = limitShutter(shutterTime); - analogueGain = status_.fixedAnalogueGain != 0.0 ? status_.fixedAnalogueGain - : exposureMode_->gain[0]; - analogueGain = limitGain(analogueGain); - if (shutterTime * analogueGain < exposureValue) { - for (unsigned int stage = 1; - stage < exposureMode_->gain.size(); stage++) { - if (!status_.fixedShutter) { - Duration stageShutter = - limitShutter(exposureMode_->shutter[stage]); - if (stageShutter * analogueGain >= exposureValue) { - shutterTime = exposureValue / analogueGain; - break; - } - shutterTime = stageShutter; - } - if (status_.fixedAnalogueGain == 0.0) { - if (exposureMode_->gain[stage] * shutterTime >= exposureValue) { - analogueGain = exposureValue / shutterTime; - break; - } - analogueGain = exposureMode_->gain[stage]; - analogueGain = limitGain(analogueGain); - } - } - } - LOG(RPiAgc, Debug) << "Divided up shutter and gain are " << shutterTime << " and " - << analogueGain; - /* - * Finally adjust shutter time for flicker avoidance (require both - * shutter and gain not to be fixed). - */ - if (!status_.fixedShutter && !status_.fixedAnalogueGain && - status_.flickerPeriod) { - int flickerPeriods = shutterTime / status_.flickerPeriod; - if (flickerPeriods) { - Duration newShutterTime = flickerPeriods * status_.flickerPeriod; - analogueGain *= shutterTime / newShutterTime; - /* - * We should still not allow the ag to go over the - * largest value in the exposure mode. Note that this - * may force more of the total exposure into the digital - * gain as a side-effect. - */ - analogueGain = std::min(analogueGain, exposureMode_->gain.back()); - analogueGain = limitGain(analogueGain); - shutterTime = newShutterTime; - } - LOG(RPiAgc, Debug) << "After flicker avoidance, shutter " - << shutterTime << " gain " << analogueGain; - } - filtered_.shutter = shutterTime; - filtered_.analogueGain = analogueGain; -} - -void Agc::writeAndFinish(Metadata *imageMetadata, bool desaturate) -{ - status_.totalExposureValue = filtered_.totalExposure; - status_.targetExposureValue = desaturate ? 0s : target_.totalExposureNoDG; - status_.shutterTime = filtered_.shutter; - status_.analogueGain = filtered_.analogueGain; - /* - * Write to metadata as well, in case anyone wants to update the camera - * immediately. - */ - imageMetadata->set("agc.status", status_); - LOG(RPiAgc, Debug) << "Output written, total exposure requested is " - << filtered_.totalExposure; - LOG(RPiAgc, Debug) << "Camera exposure update: shutter time " << filtered_.shutter - << " analogue gain " << filtered_.analogueGain; -} - -Duration Agc::limitShutter(Duration shutter) -{ - /* - * shutter == 0 is a special case for fixed shutter values, and must pass - * through unchanged - */ - if (!shutter) - return shutter; - - shutter = std::clamp(shutter, mode_.minShutter, maxShutter_); - return shutter; -} - -double Agc::limitGain(double gain) const -{ - /* - * Only limit the lower bounds of the gain value to what the sensor limits. - * The upper bound on analogue gain will be made up with additional digital - * gain applied by the ISP. - * - * gain == 0.0 is a special case for fixed shutter values, and must pass - * through unchanged - */ - if (!gain) - return gain; - - gain = std::max(gain, mode_.minAnalogueGain); - return gain; -} - -/* Register algorithm with the system. */ -static Algorithm *create(Controller *controller) -{ - return (Algorithm *)new Agc(controller); -} -static RegisterAlgorithm reg(NAME, &create); diff --git a/src/ipa/raspberrypi/controller/rpi/agc.h b/src/ipa/raspberrypi/controller/rpi/agc.h deleted file mode 100644 index 4e5f272f..00000000 --- a/src/ipa/raspberrypi/controller/rpi/agc.h +++ /dev/null @@ -1,133 +0,0 @@ -/* SPDX-License-Identifier: BSD-2-Clause */ -/* - * Copyright (C) 2019, Raspberry Pi Ltd - * - * agc.h - AGC/AEC control algorithm - */ -#pragma once - -#include -#include - -#include - -#include "../agc_algorithm.h" -#include "../agc_status.h" -#include "../pwl.h" - -/* This is our implementation of AGC. */ - -namespace RPiController { - -struct AgcMeteringMode { - std::vector weights; - int read(const libcamera::YamlObject ¶ms); -}; - -struct AgcExposureMode { - std::vector shutter; - std::vector gain; - int read(const libcamera::YamlObject ¶ms); -}; - -struct AgcConstraint { - enum class Bound { LOWER = 0, UPPER = 1 }; - Bound bound; - double qLo; - double qHi; - Pwl yTarget; - int read(const libcamera::YamlObject ¶ms); -}; - -typedef std::vector AgcConstraintMode; - -struct AgcConfig { - int read(const libcamera::YamlObject ¶ms); - std::map meteringModes; - std::map exposureModes; - std::map constraintModes; - Pwl yTarget; - double speed; - uint16_t startupFrames; - unsigned int convergenceFrames; - double maxChange; - double minChange; - double fastReduceThreshold; - double speedUpThreshold; - std::string defaultMeteringMode; - std::string defaultExposureMode; - std::string defaultConstraintMode; - double baseEv; - libcamera::utils::Duration defaultExposureTime; - double defaultAnalogueGain; -}; - -class Agc : public AgcAlgorithm -{ -public: - Agc(Controller *controller); - char const *name() const override; - int read(const libcamera::YamlObject ¶ms) override; - unsigned int getConvergenceFrames() const override; - void setEv(double ev) override; - void setFlickerPeriod(libcamera::utils::Duration flickerPeriod) override; - void setMaxShutter(libcamera::utils::Duration maxShutter) override; - void setFixedShutter(libcamera::utils::Duration fixedShutter) override; - void setFixedAnalogueGain(double fixedAnalogueGain) override; - void setMeteringMode(std::string const &meteringModeName) override; - void setExposureMode(std::string const &exposureModeName) override; - void setConstraintMode(std::string const &contraintModeName) override; - void enableAuto() override; - void disableAuto() override; - void switchMode(CameraMode const &cameraMode, Metadata *metadata) override; - void prepare(Metadata *imageMetadata) override; - void process(StatisticsPtr &stats, Metadata *imageMetadata) override; - -private: - void updateLockStatus(DeviceStatus const &deviceStatus); - AgcConfig config_; - void housekeepConfig(); - void fetchCurrentExposure(Metadata *imageMetadata); - void fetchAwbStatus(Metadata *imageMetadata); - void computeGain(StatisticsPtr &statistics, Metadata *imageMetadata, - double &gain, double &targetY); - void computeTargetExposure(double gain); - bool applyDigitalGain(double gain, double targetY); - void filterExposure(bool desaturate); - void divideUpExposure(); - void writeAndFinish(Metadata *imageMetadata, bool desaturate); - libcamera::utils::Duration limitShutter(libcamera::utils::Duration shutter); - double limitGain(double gain) const; - AgcMeteringMode *meteringMode_; - AgcExposureMode *exposureMode_; - AgcConstraintMode *constraintMode_; - CameraMode mode_; - uint64_t frameCount_; - AwbStatus awb_; - struct ExposureValues { - ExposureValues(); - - libcamera::utils::Duration shutter; - double analogueGain; - libcamera::utils::Duration totalExposure; - libcamera::utils::Duration totalExposureNoDG; /* without digital gain */ - }; - ExposureValues current_; /* values for the current frame */ - ExposureValues target_; /* calculate the values we want here */ - ExposureValues filtered_; /* these values are filtered towards target */ - AgcStatus status_; - int lockCount_; - DeviceStatus lastDeviceStatus_; - libcamera::utils::Duration lastTargetExposure_; - /* Below here the "settings" that applications can change. */ - std::string meteringModeName_; - std::string exposureModeName_; - std::string constraintModeName_; - double ev_; - libcamera::utils::Duration flickerPeriod_; - libcamera::utils::Duration maxShutter_; - libcamera::utils::Duration fixedShutter_; - double fixedAnalogueGain_; -}; - -} /* namespace RPiController */ diff --git a/src/ipa/raspberrypi/controller/rpi/alsc.cpp b/src/ipa/raspberrypi/controller/rpi/alsc.cpp deleted file mode 100644 index 3a2e8fe0..00000000 --- a/src/ipa/raspberrypi/controller/rpi/alsc.cpp +++ /dev/null @@ -1,865 +0,0 @@ -/* SPDX-License-Identifier: BSD-2-Clause */ -/* - * Copyright (C) 2019, Raspberry Pi Ltd - * - * alsc.cpp - ALSC (auto lens shading correction) control algorithm - */ - -#include -#include -#include -#include - -#include -#include - -#include "../awb_status.h" -#include "alsc.h" - -/* Raspberry Pi ALSC (Auto Lens Shading Correction) algorithm. */ - -using namespace RPiController; -using namespace libcamera; - -LOG_DEFINE_CATEGORY(RPiAlsc) - -#define NAME "rpi.alsc" - -static const double InsufficientData = -1.0; - -Alsc::Alsc(Controller *controller) - : Algorithm(controller) -{ - asyncAbort_ = asyncStart_ = asyncStarted_ = asyncFinished_ = false; - asyncThread_ = std::thread(std::bind(&Alsc::asyncFunc, this)); -} - -Alsc::~Alsc() -{ - { - std::lock_guard lock(mutex_); - asyncAbort_ = true; - } - asyncSignal_.notify_one(); - asyncThread_.join(); -} - -char const *Alsc::name() const -{ - return NAME; -} - -static int generateLut(Array2D &lut, const libcamera::YamlObject ¶ms) -{ - /* These must be signed ints for the co-ordinate calculations below. */ - int X = lut.dimensions().width, Y = lut.dimensions().height; - double cstrength = params["corner_strength"].get(2.0); - if (cstrength <= 1.0) { - LOG(RPiAlsc, Error) << "corner_strength must be > 1.0"; - return -EINVAL; - } - - double asymmetry = params["asymmetry"].get(1.0); - if (asymmetry < 0) { - LOG(RPiAlsc, Error) << "asymmetry must be >= 0"; - return -EINVAL; - } - - double f1 = cstrength - 1, f2 = 1 + sqrt(cstrength); - double R2 = X * Y / 4 * (1 + asymmetry * asymmetry); - int num = 0; - for (int y = 0; y < Y; y++) { - for (int x = 0; x < X; x++) { - double dy = y - Y / 2 + 0.5, - dx = (x - X / 2 + 0.5) * asymmetry; - double r2 = (dx * dx + dy * dy) / R2; - lut[num++] = - (f1 * r2 + f2) * (f1 * r2 + f2) / - (f2 * f2); /* this reproduces the cos^4 rule */ - } - } - return 0; -} - -static int readLut(Array2D &lut, const libcamera::YamlObject ¶ms) -{ - if (params.size() != lut.size()) { - LOG(RPiAlsc, Error) << "Invalid number of entries in LSC table"; - return -EINVAL; - } - - int num = 0; - for (const auto &p : params.asList()) { - auto value = p.get(); - if (!value) - return -EINVAL; - lut[num++] = *value; - } - - return 0; -} - -static int readCalibrations(std::vector &calibrations, - const libcamera::YamlObject ¶ms, - std::string const &name, const Size &size) -{ - if (params.contains(name)) { - double lastCt = 0; - for (const auto &p : params[name].asList()) { - auto value = p["ct"].get(); - if (!value) - return -EINVAL; - double ct = *value; - if (ct <= lastCt) { - LOG(RPiAlsc, Error) - << "Entries in " << name << " must be in increasing ct order"; - return -EINVAL; - } - AlscCalibration calibration; - calibration.ct = lastCt = ct; - - const libcamera::YamlObject &table = p["table"]; - if (table.size() != size.width * size.height) { - LOG(RPiAlsc, Error) - << "Incorrect number of values for ct " - << ct << " in " << name; - return -EINVAL; - } - - int num = 0; - calibration.table.resize(size); - for (const auto &elem : table.asList()) { - value = elem.get(); - if (!value) - return -EINVAL; - calibration.table[num++] = *value; - } - - calibrations.push_back(std::move(calibration)); - LOG(RPiAlsc, Debug) - << "Read " << name << " calibration for ct " << ct; - } - } - return 0; -} - -int Alsc::read(const libcamera::YamlObject ¶ms) -{ - config_.tableSize = getHardwareConfig().awbRegions; - config_.framePeriod = params["frame_period"].get(12); - config_.startupFrames = params["startup_frames"].get(10); - config_.speed = params["speed"].get(0.05); - double sigma = params["sigma"].get(0.01); - config_.sigmaCr = params["sigma_Cr"].get(sigma); - config_.sigmaCb = params["sigma_Cb"].get(sigma); - config_.minCount = params["min_count"].get(10.0); - config_.minG = params["min_G"].get(50); - config_.omega = params["omega"].get(1.3); - config_.nIter = params["n_iter"].get(config_.tableSize.width + config_.tableSize.height); - config_.luminanceStrength = - params["luminance_strength"].get(1.0); - - config_.luminanceLut.resize(config_.tableSize, 1.0); - int ret = 0; - - if (params.contains("corner_strength")) - ret = generateLut(config_.luminanceLut, params); - else if (params.contains("luminance_lut")) - ret = readLut(config_.luminanceLut, params["luminance_lut"]); - else - LOG(RPiAlsc, Warning) - << "no luminance table - assume unity everywhere"; - if (ret) - return ret; - - ret = readCalibrations(config_.calibrationsCr, params, "calibrations_Cr", - config_.tableSize); - if (ret) - return ret; - ret = readCalibrations(config_.calibrationsCb, params, "calibrations_Cb", - config_.tableSize); - if (ret) - return ret; - - config_.defaultCt = params["default_ct"].get(4500.0); - config_.threshold = params["threshold"].get(1e-3); - config_.lambdaBound = params["lambda_bound"].get(0.05); - - return 0; -} - -static double getCt(Metadata *metadata, double defaultCt); -static void getCalTable(double ct, std::vector const &calibrations, - Array2D &calTable); -static void resampleCalTable(const Array2D &calTableIn, CameraMode const &cameraMode, - Array2D &calTableOut); -static void compensateLambdasForCal(const Array2D &calTable, - const Array2D &oldLambdas, - Array2D &newLambdas); -static void addLuminanceToTables(std::array, 3> &results, - const Array2D &lambdaR, double lambdaG, - const Array2D &lambdaB, - const Array2D &luminanceLut, - double luminanceStrength); - -void Alsc::initialise() -{ - frameCount2_ = frameCount_ = framePhase_ = 0; - firstTime_ = true; - ct_ = config_.defaultCt; - - const size_t XY = config_.tableSize.width * config_.tableSize.height; - - for (auto &r : syncResults_) - r.resize(config_.tableSize); - for (auto &r : prevSyncResults_) - r.resize(config_.tableSize); - for (auto &r : asyncResults_) - r.resize(config_.tableSize); - - luminanceTable_.resize(config_.tableSize); - asyncLambdaR_.resize(config_.tableSize); - asyncLambdaB_.resize(config_.tableSize); - /* The lambdas are initialised in the SwitchMode. */ - lambdaR_.resize(config_.tableSize); - lambdaB_.resize(config_.tableSize); - - /* Temporaries for the computations, but sensible to allocate this up-front! */ - for (auto &c : tmpC_) - c.resize(config_.tableSize); - for (auto &m : tmpM_) - m.resize(XY); -} - -void Alsc::waitForAysncThread() -{ - if (asyncStarted_) { - asyncStarted_ = false; - std::unique_lock lock(mutex_); - syncSignal_.wait(lock, [&] { - return asyncFinished_; - }); - asyncFinished_ = false; - } -} - -static bool compareModes(CameraMode const &cm0, CameraMode const &cm1) -{ - /* - * Return true if the modes crop from the sensor significantly differently, - * or if the user transform has changed. - */ - if (cm0.transform != cm1.transform) - return true; - int leftDiff = abs(cm0.cropX - cm1.cropX); - int topDiff = abs(cm0.cropY - cm1.cropY); - int rightDiff = fabs(cm0.cropX + cm0.scaleX * cm0.width - - cm1.cropX - cm1.scaleX * cm1.width); - int bottomDiff = fabs(cm0.cropY + cm0.scaleY * cm0.height - - cm1.cropY - cm1.scaleY * cm1.height); - /* - * These thresholds are a rather arbitrary amount chosen to trigger - * when carrying on with the previously calculated tables might be - * worse than regenerating them (but without the adaptive algorithm). - */ - int thresholdX = cm0.sensorWidth >> 4; - int thresholdY = cm0.sensorHeight >> 4; - return leftDiff > thresholdX || rightDiff > thresholdX || - topDiff > thresholdY || bottomDiff > thresholdY; -} - -void Alsc::switchMode(CameraMode const &cameraMode, - [[maybe_unused]] Metadata *metadata) -{ - /* - * We're going to start over with the tables if there's any "significant" - * change. - */ - bool resetTables = firstTime_ || compareModes(cameraMode_, cameraMode); - - /* Believe the colour temperature from the AWB, if there is one. */ - ct_ = getCt(metadata, ct_); - - /* Ensure the other thread isn't running while we do this. */ - waitForAysncThread(); - - cameraMode_ = cameraMode; - - /* - * We must resample the luminance table like we do the others, but it's - * fixed so we can simply do it up front here. - */ - resampleCalTable(config_.luminanceLut, cameraMode_, luminanceTable_); - - if (resetTables) { - /* - * Upon every "table reset", arrange for something sensible to be - * generated. Construct the tables for the previous recorded colour - * temperature. In order to start over from scratch we initialise - * the lambdas, but the rest of this code then echoes the code in - * doAlsc, without the adaptive algorithm. - */ - std::fill(lambdaR_.begin(), lambdaR_.end(), 1.0); - std::fill(lambdaB_.begin(), lambdaB_.end(), 1.0); - Array2D &calTableR = tmpC_[0], &calTableB = tmpC_[1], &calTableTmp = tmpC_[2]; - getCalTable(ct_, config_.calibrationsCr, calTableTmp); - resampleCalTable(calTableTmp, cameraMode_, calTableR); - getCalTable(ct_, config_.calibrationsCb, calTableTmp); - resampleCalTable(calTableTmp, cameraMode_, calTableB); - compensateLambdasForCal(calTableR, lambdaR_, asyncLambdaR_); - compensateLambdasForCal(calTableB, lambdaB_, asyncLambdaB_); - addLuminanceToTables(syncResults_, asyncLambdaR_, 1.0, asyncLambdaB_, - luminanceTable_, config_.luminanceStrength); - prevSyncResults_ = syncResults_; - framePhase_ = config_.framePeriod; /* run the algo again asap */ - firstTime_ = false; - } -} - -void Alsc::fetchAsyncResults() -{ - LOG(RPiAlsc, Debug) << "Fetch ALSC results"; - asyncFinished_ = false; - asyncStarted_ = false; - syncResults_ = asyncResults_; -} - -double getCt(Metadata *metadata, double defaultCt) -{ - AwbStatus awbStatus; - awbStatus.temperatureK = defaultCt; /* in case nothing found */ - if (metadata->get("awb.status", awbStatus) != 0) - LOG(RPiAlsc, Debug) << "no AWB results found, using " - << awbStatus.temperatureK; - else - LOG(RPiAlsc, Debug) << "AWB results found, using " - << awbStatus.temperatureK; - return awbStatus.temperatureK; -} - -static void copyStats(RgbyRegions ®ions, StatisticsPtr &stats, - AlscStatus const &status) -{ - if (!regions.numRegions()) - regions.init(stats->awbRegions.size()); - - const std::vector &rTable = status.r; - const std::vector &gTable = status.g; - const std::vector &bTable = status.b; - for (unsigned int i = 0; i < stats->awbRegions.numRegions(); i++) { - auto r = stats->awbRegions.get(i); - r.val.rSum = static_cast(r.val.rSum / rTable[i]); - r.val.gSum = static_cast(r.val.gSum / gTable[i]); - r.val.bSum = static_cast(r.val.bSum / bTable[i]); - regions.set(i, r); - } -} - -void Alsc::restartAsync(StatisticsPtr &stats, Metadata *imageMetadata) -{ - LOG(RPiAlsc, Debug) << "Starting ALSC calculation"; - /* - * Get the current colour temperature. It's all we need from the - * metadata. Default to the last CT value (which could be the default). - */ - ct_ = getCt(imageMetadata, ct_); - /* - * We have to copy the statistics here, dividing out our best guess of - * the LSC table that the pipeline applied to them. - */ - AlscStatus alscStatus; - if (imageMetadata->get("alsc.status", alscStatus) != 0) { - LOG(RPiAlsc, Warning) - << "No ALSC status found for applied gains!"; - alscStatus.r.resize(config_.tableSize.width * config_.tableSize.height, 1.0); - alscStatus.g.resize(config_.tableSize.width * config_.tableSize.height, 1.0); - alscStatus.b.resize(config_.tableSize.width * config_.tableSize.height, 1.0); - } - copyStats(statistics_, stats, alscStatus); - framePhase_ = 0; - asyncStarted_ = true; - { - std::lock_guard lock(mutex_); - asyncStart_ = true; - } - asyncSignal_.notify_one(); -} - -void Alsc::prepare(Metadata *imageMetadata) -{ - /* - * Count frames since we started, and since we last poked the async - * thread. - */ - if (frameCount_ < (int)config_.startupFrames) - frameCount_++; - double speed = frameCount_ < (int)config_.startupFrames - ? 1.0 - : config_.speed; - LOG(RPiAlsc, Debug) - << "frame count " << frameCount_ << " speed " << speed; - { - std::unique_lock lock(mutex_); - if (asyncStarted_ && asyncFinished_) - fetchAsyncResults(); - } - /* Apply IIR filter to results and program into the pipeline. */ - for (unsigned int j = 0; j < syncResults_.size(); j++) { - for (unsigned int i = 0; i < syncResults_[j].size(); i++) - prevSyncResults_[j][i] = speed * syncResults_[j][i] + (1.0 - speed) * prevSyncResults_[j][i]; - } - /* Put output values into status metadata. */ - AlscStatus status; - status.r = prevSyncResults_[0].data(); - status.g = prevSyncResults_[1].data(); - status.b = prevSyncResults_[2].data(); - imageMetadata->set("alsc.status", status); -} - -void Alsc::process(StatisticsPtr &stats, Metadata *imageMetadata) -{ - /* - * Count frames since we started, and since we last poked the async - * thread. - */ - if (framePhase_ < (int)config_.framePeriod) - framePhase_++; - if (frameCount2_ < (int)config_.startupFrames) - frameCount2_++; - LOG(RPiAlsc, Debug) << "frame_phase " << framePhase_; - if (framePhase_ >= (int)config_.framePeriod || - frameCount2_ < (int)config_.startupFrames) { - if (asyncStarted_ == false) - restartAsync(stats, imageMetadata); - } -} - -void Alsc::asyncFunc() -{ - while (true) { - { - std::unique_lock lock(mutex_); - asyncSignal_.wait(lock, [&] { - return asyncStart_ || asyncAbort_; - }); - asyncStart_ = false; - if (asyncAbort_) - break; - } - doAlsc(); - { - std::lock_guard lock(mutex_); - asyncFinished_ = true; - } - syncSignal_.notify_one(); - } -} - -void getCalTable(double ct, std::vector const &calibrations, - Array2D &calTable) -{ - if (calibrations.empty()) { - std::fill(calTable.begin(), calTable.end(), 1.0); - LOG(RPiAlsc, Debug) << "no calibrations found"; - } else if (ct <= calibrations.front().ct) { - calTable = calibrations.front().table; - LOG(RPiAlsc, Debug) << "using calibration for " - << calibrations.front().ct; - } else if (ct >= calibrations.back().ct) { - calTable = calibrations.back().table; - LOG(RPiAlsc, Debug) << "using calibration for " - << calibrations.back().ct; - } else { - int idx = 0; - while (ct > calibrations[idx + 1].ct) - idx++; - double ct0 = calibrations[idx].ct, ct1 = calibrations[idx + 1].ct; - LOG(RPiAlsc, Debug) - << "ct is " << ct << ", interpolating between " - << ct0 << " and " << ct1; - for (unsigned int i = 0; i < calTable.size(); i++) - calTable[i] = - (calibrations[idx].table[i] * (ct1 - ct) + - calibrations[idx + 1].table[i] * (ct - ct0)) / - (ct1 - ct0); - } -} - -void resampleCalTable(const Array2D &calTableIn, - CameraMode const &cameraMode, - Array2D &calTableOut) -{ - int X = calTableIn.dimensions().width; - int Y = calTableIn.dimensions().height; - - /* - * Precalculate and cache the x sampling locations and phases to save - * recomputing them on every row. - */ - int xLo[X], xHi[X]; - double xf[X]; - double scaleX = cameraMode.sensorWidth / - (cameraMode.width * cameraMode.scaleX); - double xOff = cameraMode.cropX / (double)cameraMode.sensorWidth; - double x = .5 / scaleX + xOff * X - .5; - double xInc = 1 / scaleX; - for (int i = 0; i < X; i++, x += xInc) { - xLo[i] = floor(x); - xf[i] = x - xLo[i]; - xHi[i] = std::min(xLo[i] + 1, X - 1); - xLo[i] = std::max(xLo[i], 0); - if (!!(cameraMode.transform & libcamera::Transform::HFlip)) { - xLo[i] = X - 1 - xLo[i]; - xHi[i] = X - 1 - xHi[i]; - } - } - /* Now march over the output table generating the new values. */ - double scaleY = cameraMode.sensorHeight / - (cameraMode.height * cameraMode.scaleY); - double yOff = cameraMode.cropY / (double)cameraMode.sensorHeight; - double y = .5 / scaleY + yOff * Y - .5; - double yInc = 1 / scaleY; - for (int j = 0; j < Y; j++, y += yInc) { - int yLo = floor(y); - double yf = y - yLo; - int yHi = std::min(yLo + 1, Y - 1); - yLo = std::max(yLo, 0); - if (!!(cameraMode.transform & libcamera::Transform::VFlip)) { - yLo = Y - 1 - yLo; - yHi = Y - 1 - yHi; - } - double const *rowAbove = calTableIn.ptr() + X * yLo; - double const *rowBelow = calTableIn.ptr() + X * yHi; - double *out = calTableOut.ptr() + X * j; - for (int i = 0; i < X; i++) { - double above = rowAbove[xLo[i]] * (1 - xf[i]) + - rowAbove[xHi[i]] * xf[i]; - double below = rowBelow[xLo[i]] * (1 - xf[i]) + - rowBelow[xHi[i]] * xf[i]; - *(out++) = above * (1 - yf) + below * yf; - } - } -} - -/* Calculate chrominance statistics (R/G and B/G) for each region. */ -static void calculateCrCb(const RgbyRegions &awbRegion, Array2D &cr, - Array2D &cb, uint32_t minCount, uint16_t minG) -{ - for (unsigned int i = 0; i < cr.size(); i++) { - auto s = awbRegion.get(i); - - if (s.counted <= minCount || s.val.gSum / s.counted <= minG) { - cr[i] = cb[i] = InsufficientData; - continue; - } - - cr[i] = s.val.rSum / (double)s.val.gSum; - cb[i] = s.val.bSum / (double)s.val.gSum; - } -} - -static void applyCalTable(const Array2D &calTable, Array2D &C) -{ - for (unsigned int i = 0; i < C.size(); i++) - if (C[i] != InsufficientData) - C[i] *= calTable[i]; -} - -void compensateLambdasForCal(const Array2D &calTable, - const Array2D &oldLambdas, - Array2D &newLambdas) -{ - double minNewLambda = std::numeric_limits::max(); - for (unsigned int i = 0; i < newLambdas.size(); i++) { - newLambdas[i] = oldLambdas[i] * calTable[i]; - minNewLambda = std::min(minNewLambda, newLambdas[i]); - } - for (unsigned int i = 0; i < newLambdas.size(); i++) - newLambdas[i] /= minNewLambda; -} - -[[maybe_unused]] static void printCalTable(const Array2D &C) -{ - const Size &size = C.dimensions(); - printf("table: [\n"); - for (unsigned int j = 0; j < size.height; j++) { - for (unsigned int i = 0; i < size.width; i++) { - printf("%5.3f", 1.0 / C[j * size.width + i]); - if (i != size.width - 1 || j != size.height - 1) - printf(","); - } - printf("\n"); - } - printf("]\n"); -} - -/* - * Compute weight out of 1.0 which reflects how similar we wish to make the - * colours of these two regions. - */ -static double computeWeight(double Ci, double Cj, double sigma) -{ - if (Ci == InsufficientData || Cj == InsufficientData) - return 0; - double diff = (Ci - Cj) / sigma; - return exp(-diff * diff / 2); -} - -/* Compute all weights. */ -static void computeW(const Array2D &C, double sigma, - SparseArray &W) -{ - size_t XY = C.size(); - size_t X = C.dimensions().width; - - for (unsigned int i = 0; i < XY; i++) { - /* Start with neighbour above and go clockwise. */ - W[i][0] = i >= X ? computeWeight(C[i], C[i - X], sigma) : 0; - W[i][1] = i % X < X - 1 ? computeWeight(C[i], C[i + 1], sigma) : 0; - W[i][2] = i < XY - X ? computeWeight(C[i], C[i + X], sigma) : 0; - W[i][3] = i % X ? computeWeight(C[i], C[i - 1], sigma) : 0; - } -} - -/* Compute M, the large but sparse matrix such that M * lambdas = 0. */ -static void constructM(const Array2D &C, - const SparseArray &W, - SparseArray &M) -{ - size_t XY = C.size(); - size_t X = C.dimensions().width; - - double epsilon = 0.001; - for (unsigned int i = 0; i < XY; i++) { - /* - * Note how, if C[i] == INSUFFICIENT_DATA, the weights will all - * be zero so the equation is still set up correctly. - */ - int m = !!(i >= X) + !!(i % X < X - 1) + !!(i < XY - X) + - !!(i % X); /* total number of neighbours */ - /* we'll divide the diagonal out straight away */ - double diagonal = (epsilon + W[i][0] + W[i][1] + W[i][2] + W[i][3]) * C[i]; - M[i][0] = i >= X ? (W[i][0] * C[i - X] + epsilon / m * C[i]) / diagonal : 0; - M[i][1] = i % X < X - 1 ? (W[i][1] * C[i + 1] + epsilon / m * C[i]) / diagonal : 0; - M[i][2] = i < XY - X ? (W[i][2] * C[i + X] + epsilon / m * C[i]) / diagonal : 0; - M[i][3] = i % X ? (W[i][3] * C[i - 1] + epsilon / m * C[i]) / diagonal : 0; - } -} - -/* - * In the compute_lambda_ functions, note that the matrix coefficients for the - * left/right neighbours are zero down the left/right edges, so we don't need - * need to test the i value to exclude them. - */ -static double computeLambdaBottom(int i, const SparseArray &M, - Array2D &lambda) -{ - return M[i][1] * lambda[i + 1] + M[i][2] * lambda[i + lambda.dimensions().width] + - M[i][3] * lambda[i - 1]; -} -static double computeLambdaBottomStart(int i, const SparseArray &M, - Array2D &lambda) -{ - return M[i][1] * lambda[i + 1] + M[i][2] * lambda[i + lambda.dimensions().width]; -} -static double computeLambdaInterior(int i, const SparseArray &M, - Array2D &lambda) -{ - return M[i][0] * lambda[i - lambda.dimensions().width] + M[i][1] * lambda[i + 1] + - M[i][2] * lambda[i + lambda.dimensions().width] + M[i][3] * lambda[i - 1]; -} -static double computeLambdaTop(int i, const SparseArray &M, - Array2D &lambda) -{ - return M[i][0] * lambda[i - lambda.dimensions().width] + M[i][1] * lambda[i + 1] + - M[i][3] * lambda[i - 1]; -} -static double computeLambdaTopEnd(int i, const SparseArray &M, - Array2D &lambda) -{ - return M[i][0] * lambda[i - lambda.dimensions().width] + M[i][3] * lambda[i - 1]; -} - -/* Gauss-Seidel iteration with over-relaxation. */ -static double gaussSeidel2Sor(const SparseArray &M, double omega, - Array2D &lambda, double lambdaBound) -{ - int XY = lambda.size(); - int X = lambda.dimensions().width; - const double min = 1 - lambdaBound, max = 1 + lambdaBound; - Array2D oldLambda = lambda; - int i; - lambda[0] = computeLambdaBottomStart(0, M, lambda); - lambda[0] = std::clamp(lambda[0], min, max); - for (i = 1; i < X; i++) { - lambda[i] = computeLambdaBottom(i, M, lambda); - lambda[i] = std::clamp(lambda[i], min, max); - } - for (; i < XY - X; i++) { - lambda[i] = computeLambdaInterior(i, M, lambda); - lambda[i] = std::clamp(lambda[i], min, max); - } - for (; i < XY - 1; i++) { - lambda[i] = computeLambdaTop(i, M, lambda); - lambda[i] = std::clamp(lambda[i], min, max); - } - lambda[i] = computeLambdaTopEnd(i, M, lambda); - lambda[i] = std::clamp(lambda[i], min, max); - /* - * Also solve the system from bottom to top, to help spread the updates - * better. - */ - lambda[i] = computeLambdaTopEnd(i, M, lambda); - lambda[i] = std::clamp(lambda[i], min, max); - for (i = XY - 2; i >= XY - X; i--) { - lambda[i] = computeLambdaTop(i, M, lambda); - lambda[i] = std::clamp(lambda[i], min, max); - } - for (; i >= X; i--) { - lambda[i] = computeLambdaInterior(i, M, lambda); - lambda[i] = std::clamp(lambda[i], min, max); - } - for (; i >= 1; i--) { - lambda[i] = computeLambdaBottom(i, M, lambda); - lambda[i] = std::clamp(lambda[i], min, max); - } - lambda[0] = computeLambdaBottomStart(0, M, lambda); - lambda[0] = std::clamp(lambda[0], min, max); - double maxDiff = 0; - for (i = 0; i < XY; i++) { - lambda[i] = oldLambda[i] + (lambda[i] - oldLambda[i]) * omega; - if (fabs(lambda[i] - oldLambda[i]) > fabs(maxDiff)) - maxDiff = lambda[i] - oldLambda[i]; - } - return maxDiff; -} - -/* Normalise the values so that the smallest value is 1. */ -static void normalise(Array2D &results) -{ - double minval = *std::min_element(results.begin(), results.end()); - std::for_each(results.begin(), results.end(), - [minval](double val) { return val / minval; }); -} - -/* Rescale the values so that the average value is 1. */ -static void reaverage(Array2D &data) -{ - double sum = std::accumulate(data.begin(), data.end(), 0.0); - double ratio = 1 / (sum / data.size()); - std::for_each(data.begin(), data.end(), - [ratio](double val) { return val * ratio; }); -} - -static void runMatrixIterations(const Array2D &C, - Array2D &lambda, - const SparseArray &W, - SparseArray &M, double omega, - unsigned int nIter, double threshold, double lambdaBound) -{ - constructM(C, W, M); - double lastMaxDiff = std::numeric_limits::max(); - for (unsigned int i = 0; i < nIter; i++) { - double maxDiff = fabs(gaussSeidel2Sor(M, omega, lambda, lambdaBound)); - if (maxDiff < threshold) { - LOG(RPiAlsc, Debug) - << "Stop after " << i + 1 << " iterations"; - break; - } - /* - * this happens very occasionally (so make a note), though - * doesn't seem to matter - */ - if (maxDiff > lastMaxDiff) - LOG(RPiAlsc, Debug) - << "Iteration " << i << ": maxDiff gone up " - << lastMaxDiff << " to " << maxDiff; - lastMaxDiff = maxDiff; - } - /* We're going to normalise the lambdas so the total average is 1. */ - reaverage(lambda); -} - -static void addLuminanceRb(Array2D &result, const Array2D &lambda, - const Array2D &luminanceLut, - double luminanceStrength) -{ - for (unsigned int i = 0; i < result.size(); i++) - result[i] = lambda[i] * ((luminanceLut[i] - 1) * luminanceStrength + 1); -} - -static void addLuminanceG(Array2D &result, double lambda, - const Array2D &luminanceLut, - double luminanceStrength) -{ - for (unsigned int i = 0; i < result.size(); i++) - result[i] = lambda * ((luminanceLut[i] - 1) * luminanceStrength + 1); -} - -void addLuminanceToTables(std::array, 3> &results, - const Array2D &lambdaR, - double lambdaG, const Array2D &lambdaB, - const Array2D &luminanceLut, - double luminanceStrength) -{ - addLuminanceRb(results[0], lambdaR, luminanceLut, luminanceStrength); - addLuminanceG(results[1], lambdaG, luminanceLut, luminanceStrength); - addLuminanceRb(results[2], lambdaB, luminanceLut, luminanceStrength); - for (auto &r : results) - normalise(r); -} - -void Alsc::doAlsc() -{ - Array2D &cr = tmpC_[0], &cb = tmpC_[1], &calTableR = tmpC_[2], - &calTableB = tmpC_[3], &calTableTmp = tmpC_[4]; - SparseArray &wr = tmpM_[0], &wb = tmpM_[1], &M = tmpM_[2]; - - /* - * Calculate our R/B ("Cr"/"Cb") colour statistics, and assess which are - * usable. - */ - calculateCrCb(statistics_, cr, cb, config_.minCount, config_.minG); - /* - * Fetch the new calibrations (if any) for this CT. Resample them in - * case the camera mode is not full-frame. - */ - getCalTable(ct_, config_.calibrationsCr, calTableTmp); - resampleCalTable(calTableTmp, cameraMode_, calTableR); - getCalTable(ct_, config_.calibrationsCb, calTableTmp); - resampleCalTable(calTableTmp, cameraMode_, calTableB); - /* - * You could print out the cal tables for this image here, if you're - * tuning the algorithm... - * Apply any calibration to the statistics, so the adaptive algorithm - * makes only the extra adjustments. - */ - applyCalTable(calTableR, cr); - applyCalTable(calTableB, cb); - /* Compute weights between zones. */ - computeW(cr, config_.sigmaCr, wr); - computeW(cb, config_.sigmaCb, wb); - /* Run Gauss-Seidel iterations over the resulting matrix, for R and B. */ - runMatrixIterations(cr, lambdaR_, wr, M, config_.omega, config_.nIter, - config_.threshold, config_.lambdaBound); - runMatrixIterations(cb, lambdaB_, wb, M, config_.omega, config_.nIter, - config_.threshold, config_.lambdaBound); - /* - * Fold the calibrated gains into our final lambda values. (Note that on - * the next run, we re-start with the lambda values that don't have the - * calibration gains included.) - */ - compensateLambdasForCal(calTableR, lambdaR_, asyncLambdaR_); - compensateLambdasForCal(calTableB, lambdaB_, asyncLambdaB_); - /* Fold in the luminance table at the appropriate strength. */ - addLuminanceToTables(asyncResults_, asyncLambdaR_, 1.0, - asyncLambdaB_, luminanceTable_, - config_.luminanceStrength); -} - -/* Register algorithm with the system. */ -static Algorithm *create(Controller *controller) -{ - return (Algorithm *)new Alsc(controller); -} -static RegisterAlgorithm reg(NAME, &create); diff --git a/src/ipa/raspberrypi/controller/rpi/alsc.h b/src/ipa/raspberrypi/controller/rpi/alsc.h deleted file mode 100644 index 0b6d9478..00000000 --- a/src/ipa/raspberrypi/controller/rpi/alsc.h +++ /dev/null @@ -1,174 +0,0 @@ -/* SPDX-License-Identifier: BSD-2-Clause */ -/* - * Copyright (C) 2019, Raspberry Pi Ltd - * - * alsc.h - ALSC (auto lens shading correction) control algorithm - */ -#pragma once - -#include -#include -#include -#include -#include - -#include - -#include "../algorithm.h" -#include "../alsc_status.h" -#include "../statistics.h" - -namespace RPiController { - -/* Algorithm to generate automagic LSC (Lens Shading Correction) tables. */ - -/* - * The Array2D class is a very thin wrapper round std::vector so that it can - * be used in exactly the same way in the code but carries its correct width - * and height ("dimensions") with it. - */ - -template -class Array2D -{ -public: - using Size = libcamera::Size; - - const Size &dimensions() const { return dimensions_; } - - size_t size() const { return data_.size(); } - - const std::vector &data() const { return data_; } - - void resize(const Size &dims) - { - dimensions_ = dims; - data_.resize(dims.width * dims.height); - } - - void resize(const Size &dims, const T &value) - { - resize(dims); - std::fill(data_.begin(), data_.end(), value); - } - - T &operator[](int index) { return data_[index]; } - - const T &operator[](int index) const { return data_[index]; } - - T *ptr() { return data_.data(); } - - const T *ptr() const { return data_.data(); } - - auto begin() { return data_.begin(); } - auto end() { return data_.end(); } - -private: - Size dimensions_; - std::vector data_; -}; - -/* - * We'll use the term SparseArray for the large sparse matrices that are - * XY tall but have only 4 non-zero elements on each row. - */ - -template -using SparseArray = std::vector>; - -struct AlscCalibration { - double ct; - Array2D table; -}; - -struct AlscConfig { - /* Only repeat the ALSC calculation every "this many" frames */ - uint16_t framePeriod; - /* number of initial frames for which speed taken as 1.0 (maximum) */ - uint16_t startupFrames; - /* IIR filter speed applied to algorithm results */ - double speed; - double sigmaCr; - double sigmaCb; - double minCount; - uint16_t minG; - double omega; - uint32_t nIter; - Array2D luminanceLut; - double luminanceStrength; - std::vector calibrationsCr; - std::vector calibrationsCb; - double defaultCt; /* colour temperature if no metadata found */ - double threshold; /* iteration termination threshold */ - double lambdaBound; /* upper/lower bound for lambda from a value of 1 */ - libcamera::Size tableSize; -}; - -class Alsc : public Algorithm -{ -public: - Alsc(Controller *controller = NULL); - ~Alsc(); - char const *name() const override; - void initialise() override; - void switchMode(CameraMode const &cameraMode, Metadata *metadata) override; - int read(const libcamera::YamlObject ¶ms) override; - void prepare(Metadata *imageMetadata) override; - void process(StatisticsPtr &stats, Metadata *imageMetadata) override; - -private: - /* configuration is read-only, and available to both threads */ - AlscConfig config_; - bool firstTime_; - CameraMode cameraMode_; - Array2D luminanceTable_; - std::thread asyncThread_; - void asyncFunc(); /* asynchronous thread function */ - std::mutex mutex_; - /* condvar for async thread to wait on */ - std::condition_variable asyncSignal_; - /* condvar for synchronous thread to wait on */ - std::condition_variable syncSignal_; - /* for sync thread to check if async thread finished (requires mutex) */ - bool asyncFinished_; - /* for async thread to check if it's been told to run (requires mutex) */ - bool asyncStart_; - /* for async thread to check if it's been told to quit (requires mutex) */ - bool asyncAbort_; - - /* - * The following are only for the synchronous thread to use: - * for sync thread to note its has asked async thread to run - */ - bool asyncStarted_; - /* counts up to framePeriod before restarting the async thread */ - int framePhase_; - /* counts up to startupFrames */ - int frameCount_; - /* counts up to startupFrames for Process function */ - int frameCount2_; - std::array, 3> syncResults_; - std::array, 3> prevSyncResults_; - void waitForAysncThread(); - /* - * The following are for the asynchronous thread to use, though the main - * thread can set/reset them if the async thread is known to be idle: - */ - void restartAsync(StatisticsPtr &stats, Metadata *imageMetadata); - /* copy out the results from the async thread so that it can be restarted */ - void fetchAsyncResults(); - double ct_; - RgbyRegions statistics_; - std::array, 3> asyncResults_; - Array2D asyncLambdaR_; - Array2D asyncLambdaB_; - void doAlsc(); - Array2D lambdaR_; - Array2D lambdaB_; - - /* Temporaries for the computations */ - std::array, 5> tmpC_; - std::array, 3> tmpM_; -}; - -} /* namespace RPiController */ diff --git a/src/ipa/raspberrypi/controller/rpi/awb.cpp b/src/ipa/raspberrypi/controller/rpi/awb.cpp deleted file mode 100644 index ef3435d6..00000000 --- a/src/ipa/raspberrypi/controller/rpi/awb.cpp +++ /dev/null @@ -1,734 +0,0 @@ -/* SPDX-License-Identifier: BSD-2-Clause */ -/* - * Copyright (C) 2019, Raspberry Pi Ltd - * - * awb.cpp - AWB control algorithm - */ - -#include -#include - -#include - -#include "../lux_status.h" - -#include "awb.h" - -using namespace RPiController; -using namespace libcamera; - -LOG_DEFINE_CATEGORY(RPiAwb) - -#define NAME "rpi.awb" - -/* - * todo - the locking in this algorithm needs some tidying up as has been done - * elsewhere (ALSC and AGC). - */ - -int AwbMode::read(const libcamera::YamlObject ¶ms) -{ - auto value = params["lo"].get(); - if (!value) - return -EINVAL; - ctLo = *value; - - value = params["hi"].get(); - if (!value) - return -EINVAL; - ctHi = *value; - - return 0; -} - -int AwbPrior::read(const libcamera::YamlObject ¶ms) -{ - auto value = params["lux"].get(); - if (!value) - return -EINVAL; - lux = *value; - - return prior.read(params["prior"]); -} - -static int readCtCurve(Pwl &ctR, Pwl &ctB, const libcamera::YamlObject ¶ms) -{ - if (params.size() % 3) { - LOG(RPiAwb, Error) << "AwbConfig: incomplete CT curve entry"; - return -EINVAL; - } - - if (params.size() < 6) { - LOG(RPiAwb, Error) << "AwbConfig: insufficient points in CT curve"; - return -EINVAL; - } - - const auto &list = params.asList(); - - for (auto it = list.begin(); it != list.end(); it++) { - auto value = it->get(); - if (!value) - return -EINVAL; - double ct = *value; - - assert(it == list.begin() || ct != ctR.domain().end); - - value = (++it)->get(); - if (!value) - return -EINVAL; - ctR.append(ct, *value); - - value = (++it)->get(); - if (!value) - return -EINVAL; - ctB.append(ct, *value); - } - - return 0; -} - -int AwbConfig::read(const libcamera::YamlObject ¶ms) -{ - int ret; - - bayes = params["bayes"].get(1); - framePeriod = params["frame_period"].get(10); - startupFrames = params["startup_frames"].get(10); - convergenceFrames = params["convergence_frames"].get(3); - speed = params["speed"].get(0.05); - - if (params.contains("ct_curve")) { - ret = readCtCurve(ctR, ctB, params["ct_curve"]); - if (ret) - return ret; - /* We will want the inverse functions of these too. */ - ctRInverse = ctR.inverse(); - ctBInverse = ctB.inverse(); - } - - if (params.contains("priors")) { - for (const auto &p : params["priors"].asList()) { - AwbPrior prior; - ret = prior.read(p); - if (ret) - return ret; - if (!priors.empty() && prior.lux <= priors.back().lux) { - LOG(RPiAwb, Error) << "AwbConfig: Prior must be ordered in increasing lux value"; - return -EINVAL; - } - priors.push_back(prior); - } - if (priors.empty()) { - LOG(RPiAwb, Error) << "AwbConfig: no AWB priors configured"; - return ret; - } - } - if (params.contains("modes")) { - for (const auto &[key, value] : params["modes"].asDict()) { - ret = modes[key].read(value); - if (ret) - return ret; - if (defaultMode == nullptr) - defaultMode = &modes[key]; - } - if (defaultMode == nullptr) { - LOG(RPiAwb, Error) << "AwbConfig: no AWB modes configured"; - return -EINVAL; - } - } - - minPixels = params["min_pixels"].get(16.0); - minG = params["min_G"].get(32); - minRegions = params["min_regions"].get(10); - deltaLimit = params["delta_limit"].get(0.2); - coarseStep = params["coarse_step"].get(0.2); - transversePos = params["transverse_pos"].get(0.01); - transverseNeg = params["transverse_neg"].get(0.01); - if (transversePos <= 0 || transverseNeg <= 0) { - LOG(RPiAwb, Error) << "AwbConfig: transverse_pos/neg must be > 0"; - return -EINVAL; - } - - sensitivityR = params["sensitivity_r"].get(1.0); - sensitivityB = params["sensitivity_b"].get(1.0); - - if (bayes) { - if (ctR.empty() || ctB.empty() || priors.empty() || - defaultMode == nullptr) { - LOG(RPiAwb, Warning) - << "Bayesian AWB mis-configured - switch to Grey method"; - bayes = false; - } - } - fast = params[fast].get(bayes); /* default to fast for Bayesian, otherwise slow */ - whitepointR = params["whitepoint_r"].get(0.0); - whitepointB = params["whitepoint_b"].get(0.0); - if (bayes == false) - sensitivityR = sensitivityB = 1.0; /* nor do sensitivities make any sense */ - return 0; -} - -Awb::Awb(Controller *controller) - : AwbAlgorithm(controller) -{ - asyncAbort_ = asyncStart_ = asyncStarted_ = asyncFinished_ = false; - mode_ = nullptr; - manualR_ = manualB_ = 0.0; - asyncThread_ = std::thread(std::bind(&Awb::asyncFunc, this)); -} - -Awb::~Awb() -{ - { - std::lock_guard lock(mutex_); - asyncAbort_ = true; - } - asyncSignal_.notify_one(); - asyncThread_.join(); -} - -char const *Awb::name() const -{ - return NAME; -} - -int Awb::read(const libcamera::YamlObject ¶ms) -{ - return config_.read(params); -} - -void Awb::initialise() -{ - frameCount_ = framePhase_ = 0; - /* - * Put something sane into the status that we are filtering towards, - * just in case the first few frames don't have anything meaningful in - * them. - */ - if (!config_.ctR.empty() && !config_.ctB.empty()) { - syncResults_.temperatureK = config_.ctR.domain().clip(4000); - syncResults_.gainR = 1.0 / config_.ctR.eval(syncResults_.temperatureK); - syncResults_.gainG = 1.0; - syncResults_.gainB = 1.0 / config_.ctB.eval(syncResults_.temperatureK); - } else { - /* random values just to stop the world blowing up */ - syncResults_.temperatureK = 4500; - syncResults_.gainR = syncResults_.gainG = syncResults_.gainB = 1.0; - } - prevSyncResults_ = syncResults_; - asyncResults_ = syncResults_; -} - -void Awb::disableAuto() -{ - /* Freeze the most recent values, and treat them as manual gains */ - manualR_ = syncResults_.gainR = prevSyncResults_.gainR; - manualB_ = syncResults_.gainB = prevSyncResults_.gainB; - syncResults_.gainG = prevSyncResults_.gainG; - syncResults_.temperatureK = prevSyncResults_.temperatureK; -} - -void Awb::enableAuto() -{ - manualR_ = 0.0; - manualB_ = 0.0; -} - -unsigned int Awb::getConvergenceFrames() const -{ - /* - * If not in auto mode, there is no convergence - * to happen, so no need to drop any frames - return zero. - */ - if (!isAutoEnabled()) - return 0; - else - return config_.convergenceFrames; -} - -void Awb::setMode(std::string const &modeName) -{ - modeName_ = modeName; -} - -void Awb::setManualGains(double manualR, double manualB) -{ - /* If any of these are 0.0, we swich back to auto. */ - manualR_ = manualR; - manualB_ = manualB; - /* - * If not in auto mode, set these values into the syncResults which - * means that Prepare() will adopt them immediately. - */ - if (!isAutoEnabled()) { - syncResults_.gainR = prevSyncResults_.gainR = manualR_; - syncResults_.gainG = prevSyncResults_.gainG = 1.0; - syncResults_.gainB = prevSyncResults_.gainB = manualB_; - if (config_.bayes) { - /* Also estimate the best corresponding colour temperature from the curves. */ - double ctR = config_.ctRInverse.eval(config_.ctRInverse.domain().clip(1 / manualR_)); - double ctB = config_.ctBInverse.eval(config_.ctBInverse.domain().clip(1 / manualB_)); - prevSyncResults_.temperatureK = (ctR + ctB) / 2; - syncResults_.temperatureK = prevSyncResults_.temperatureK; - } - } -} - -void Awb::switchMode([[maybe_unused]] CameraMode const &cameraMode, - Metadata *metadata) -{ - /* Let other algorithms know the current white balance values. */ - metadata->set("awb.status", prevSyncResults_); -} - -bool Awb::isAutoEnabled() const -{ - return manualR_ == 0.0 || manualB_ == 0.0; -} - -void Awb::fetchAsyncResults() -{ - LOG(RPiAwb, Debug) << "Fetch AWB results"; - asyncFinished_ = false; - asyncStarted_ = false; - /* - * It's possible manual gains could be set even while the async - * thread was running, so only copy the results if still in auto mode. - */ - if (isAutoEnabled()) - syncResults_ = asyncResults_; -} - -void Awb::restartAsync(StatisticsPtr &stats, double lux) -{ - LOG(RPiAwb, Debug) << "Starting AWB calculation"; - /* this makes a new reference which belongs to the asynchronous thread */ - statistics_ = stats; - /* store the mode as it could technically change */ - auto m = config_.modes.find(modeName_); - mode_ = m != config_.modes.end() - ? &m->second - : (mode_ == nullptr ? config_.defaultMode : mode_); - lux_ = lux; - framePhase_ = 0; - asyncStarted_ = true; - size_t len = modeName_.copy(asyncResults_.mode, - sizeof(asyncResults_.mode) - 1); - asyncResults_.mode[len] = '\0'; - { - std::lock_guard lock(mutex_); - asyncStart_ = true; - } - asyncSignal_.notify_one(); -} - -void Awb::prepare(Metadata *imageMetadata) -{ - if (frameCount_ < (int)config_.startupFrames) - frameCount_++; - double speed = frameCount_ < (int)config_.startupFrames - ? 1.0 - : config_.speed; - LOG(RPiAwb, Debug) - << "frame_count " << frameCount_ << " speed " << speed; - { - std::unique_lock lock(mutex_); - if (asyncStarted_ && asyncFinished_) - fetchAsyncResults(); - } - /* Finally apply IIR filter to results and put into metadata. */ - memcpy(prevSyncResults_.mode, syncResults_.mode, - sizeof(prevSyncResults_.mode)); - prevSyncResults_.temperatureK = speed * syncResults_.temperatureK + - (1.0 - speed) * prevSyncResults_.temperatureK; - prevSyncResults_.gainR = speed * syncResults_.gainR + - (1.0 - speed) * prevSyncResults_.gainR; - prevSyncResults_.gainG = speed * syncResults_.gainG + - (1.0 - speed) * prevSyncResults_.gainG; - prevSyncResults_.gainB = speed * syncResults_.gainB + - (1.0 - speed) * prevSyncResults_.gainB; - imageMetadata->set("awb.status", prevSyncResults_); - LOG(RPiAwb, Debug) - << "Using AWB gains r " << prevSyncResults_.gainR << " g " - << prevSyncResults_.gainG << " b " - << prevSyncResults_.gainB; -} - -void Awb::process(StatisticsPtr &stats, Metadata *imageMetadata) -{ - /* Count frames since we last poked the async thread. */ - if (framePhase_ < (int)config_.framePeriod) - framePhase_++; - LOG(RPiAwb, Debug) << "frame_phase " << framePhase_; - /* We do not restart the async thread if we're not in auto mode. */ - if (isAutoEnabled() && - (framePhase_ >= (int)config_.framePeriod || - frameCount_ < (int)config_.startupFrames)) { - /* Update any settings and any image metadata that we need. */ - struct LuxStatus luxStatus = {}; - luxStatus.lux = 400; /* in case no metadata */ - if (imageMetadata->get("lux.status", luxStatus) != 0) - LOG(RPiAwb, Debug) << "No lux metadata found"; - LOG(RPiAwb, Debug) << "Awb lux value is " << luxStatus.lux; - - if (asyncStarted_ == false) - restartAsync(stats, luxStatus.lux); - } -} - -void Awb::asyncFunc() -{ - while (true) { - { - std::unique_lock lock(mutex_); - asyncSignal_.wait(lock, [&] { - return asyncStart_ || asyncAbort_; - }); - asyncStart_ = false; - if (asyncAbort_) - break; - } - doAwb(); - { - std::lock_guard lock(mutex_); - asyncFinished_ = true; - } - syncSignal_.notify_one(); - } -} - -static void generateStats(std::vector &zones, - RgbyRegions &stats, double minPixels, - double minG) -{ - for (auto const ®ion : stats) { - Awb::RGB zone; - if (region.counted >= minPixels) { - zone.G = region.val.gSum / region.counted; - if (zone.G >= minG) { - zone.R = region.val.rSum / region.counted; - zone.B = region.val.bSum / region.counted; - zones.push_back(zone); - } - } - } -} - -void Awb::prepareStats() -{ - zones_.clear(); - /* - * LSC has already been applied to the stats in this pipeline, so stop - * any LSC compensation. We also ignore config_.fast in this version. - */ - generateStats(zones_, statistics_->awbRegions, config_.minPixels, - config_.minG); - /* - * apply sensitivities, so values appear to come from our "canonical" - * sensor. - */ - for (auto &zone : zones_) { - zone.R *= config_.sensitivityR; - zone.B *= config_.sensitivityB; - } -} - -double Awb::computeDelta2Sum(double gainR, double gainB) -{ - /* - * Compute the sum of the squared colour error (non-greyness) as it - * appears in the log likelihood equation. - */ - double delta2Sum = 0; - for (auto &z : zones_) { - double deltaR = gainR * z.R - 1 - config_.whitepointR; - double deltaB = gainB * z.B - 1 - config_.whitepointB; - double delta2 = deltaR * deltaR + deltaB * deltaB; - /* LOG(RPiAwb, Debug) << "deltaR " << deltaR << " deltaB " << deltaB << " delta2 " << delta2; */ - delta2 = std::min(delta2, config_.deltaLimit); - delta2Sum += delta2; - } - return delta2Sum; -} - -Pwl Awb::interpolatePrior() -{ - /* - * Interpolate the prior log likelihood function for our current lux - * value. - */ - if (lux_ <= config_.priors.front().lux) - return config_.priors.front().prior; - else if (lux_ >= config_.priors.back().lux) - return config_.priors.back().prior; - else { - int idx = 0; - /* find which two we lie between */ - while (config_.priors[idx + 1].lux < lux_) - idx++; - double lux0 = config_.priors[idx].lux, - lux1 = config_.priors[idx + 1].lux; - return Pwl::combine(config_.priors[idx].prior, - config_.priors[idx + 1].prior, - [&](double /*x*/, double y0, double y1) { - return y0 + (y1 - y0) * - (lux_ - lux0) / (lux1 - lux0); - }); - } -} - -static double interpolateQuadatric(Pwl::Point const &a, Pwl::Point const &b, - Pwl::Point const &c) -{ - /* - * Given 3 points on a curve, find the extremum of the function in that - * interval by fitting a quadratic. - */ - const double eps = 1e-3; - Pwl::Point ca = c - a, ba = b - a; - double denominator = 2 * (ba.y * ca.x - ca.y * ba.x); - if (abs(denominator) > eps) { - double numerator = ba.y * ca.x * ca.x - ca.y * ba.x * ba.x; - double result = numerator / denominator + a.x; - return std::max(a.x, std::min(c.x, result)); - } - /* has degenerated to straight line segment */ - return a.y < c.y - eps ? a.x : (c.y < a.y - eps ? c.x : b.x); -} - -double Awb::coarseSearch(Pwl const &prior) -{ - points_.clear(); /* assume doesn't deallocate memory */ - size_t bestPoint = 0; - double t = mode_->ctLo; - int spanR = 0, spanB = 0; - /* Step down the CT curve evaluating log likelihood. */ - while (true) { - double r = config_.ctR.eval(t, &spanR); - double b = config_.ctB.eval(t, &spanB); - double gainR = 1 / r, gainB = 1 / b; - double delta2Sum = computeDelta2Sum(gainR, gainB); - double priorLogLikelihood = prior.eval(prior.domain().clip(t)); - double finalLogLikelihood = delta2Sum - priorLogLikelihood; - LOG(RPiAwb, Debug) - << "t: " << t << " gain R " << gainR << " gain B " - << gainB << " delta2_sum " << delta2Sum - << " prior " << priorLogLikelihood << " final " - << finalLogLikelihood; - points_.push_back(Pwl::Point(t, finalLogLikelihood)); - if (points_.back().y < points_[bestPoint].y) - bestPoint = points_.size() - 1; - if (t == mode_->ctHi) - break; - /* for even steps along the r/b curve scale them by the current t */ - t = std::min(t + t / 10 * config_.coarseStep, mode_->ctHi); - } - t = points_[bestPoint].x; - LOG(RPiAwb, Debug) << "Coarse search found CT " << t; - /* - * We have the best point of the search, but refine it with a quadratic - * interpolation around its neighbours. - */ - if (points_.size() > 2) { - unsigned long bp = std::min(bestPoint, points_.size() - 2); - bestPoint = std::max(1UL, bp); - t = interpolateQuadatric(points_[bestPoint - 1], - points_[bestPoint], - points_[bestPoint + 1]); - LOG(RPiAwb, Debug) - << "After quadratic refinement, coarse search has CT " - << t; - } - return t; -} - -void Awb::fineSearch(double &t, double &r, double &b, Pwl const &prior) -{ - int spanR = -1, spanB = -1; - config_.ctR.eval(t, &spanR); - config_.ctB.eval(t, &spanB); - double step = t / 10 * config_.coarseStep * 0.1; - int nsteps = 5; - double rDiff = config_.ctR.eval(t + nsteps * step, &spanR) - - config_.ctR.eval(t - nsteps * step, &spanR); - double bDiff = config_.ctB.eval(t + nsteps * step, &spanB) - - config_.ctB.eval(t - nsteps * step, &spanB); - Pwl::Point transverse(bDiff, -rDiff); - if (transverse.len2() < 1e-6) - return; - /* - * unit vector orthogonal to the b vs. r function (pointing outwards - * with r and b increasing) - */ - transverse = transverse / transverse.len(); - double bestLogLikelihood = 0, bestT = 0, bestR = 0, bestB = 0; - double transverseRange = config_.transverseNeg + config_.transversePos; - const int maxNumDeltas = 12; - /* a transverse step approximately every 0.01 r/b units */ - int numDeltas = floor(transverseRange * 100 + 0.5) + 1; - numDeltas = numDeltas < 3 ? 3 : (numDeltas > maxNumDeltas ? maxNumDeltas : numDeltas); - /* - * Step down CT curve. March a bit further if the transverse range is - * large. - */ - nsteps += numDeltas; - for (int i = -nsteps; i <= nsteps; i++) { - double tTest = t + i * step; - double priorLogLikelihood = - prior.eval(prior.domain().clip(tTest)); - double rCurve = config_.ctR.eval(tTest, &spanR); - double bCurve = config_.ctB.eval(tTest, &spanB); - /* x will be distance off the curve, y the log likelihood there */ - Pwl::Point points[maxNumDeltas]; - int bestPoint = 0; - /* Take some measurements transversely *off* the CT curve. */ - for (int j = 0; j < numDeltas; j++) { - points[j].x = -config_.transverseNeg + - (transverseRange * j) / (numDeltas - 1); - Pwl::Point rbTest = Pwl::Point(rCurve, bCurve) + - transverse * points[j].x; - double rTest = rbTest.x, bTest = rbTest.y; - double gainR = 1 / rTest, gainB = 1 / bTest; - double delta2Sum = computeDelta2Sum(gainR, gainB); - points[j].y = delta2Sum - priorLogLikelihood; - LOG(RPiAwb, Debug) - << "At t " << tTest << " r " << rTest << " b " - << bTest << ": " << points[j].y; - if (points[j].y < points[bestPoint].y) - bestPoint = j; - } - /* - * We have NUM_DELTAS points transversely across the CT curve, - * now let's do a quadratic interpolation for the best result. - */ - bestPoint = std::max(1, std::min(bestPoint, numDeltas - 2)); - Pwl::Point rbTest = Pwl::Point(rCurve, bCurve) + - transverse * interpolateQuadatric(points[bestPoint - 1], - points[bestPoint], - points[bestPoint + 1]); - double rTest = rbTest.x, bTest = rbTest.y; - double gainR = 1 / rTest, gainB = 1 / bTest; - double delta2Sum = computeDelta2Sum(gainR, gainB); - double finalLogLikelihood = delta2Sum - priorLogLikelihood; - LOG(RPiAwb, Debug) - << "Finally " - << tTest << " r " << rTest << " b " << bTest << ": " - << finalLogLikelihood - << (finalLogLikelihood < bestLogLikelihood ? " BEST" : ""); - if (bestT == 0 || finalLogLikelihood < bestLogLikelihood) - bestLogLikelihood = finalLogLikelihood, - bestT = tTest, bestR = rTest, bestB = bTest; - } - t = bestT, r = bestR, b = bestB; - LOG(RPiAwb, Debug) - << "Fine search found t " << t << " r " << r << " b " << b; -} - -void Awb::awbBayes() -{ - /* - * May as well divide out G to save computeDelta2Sum from doing it over - * and over. - */ - for (auto &z : zones_) - z.R = z.R / (z.G + 1), z.B = z.B / (z.G + 1); - /* - * Get the current prior, and scale according to how many zones are - * valid... not entirely sure about this. - */ - Pwl prior = interpolatePrior(); - prior *= zones_.size() / (double)(statistics_->awbRegions.numRegions()); - prior.map([](double x, double y) { - LOG(RPiAwb, Debug) << "(" << x << "," << y << ")"; - }); - double t = coarseSearch(prior); - double r = config_.ctR.eval(t); - double b = config_.ctB.eval(t); - LOG(RPiAwb, Debug) - << "After coarse search: r " << r << " b " << b << " (gains r " - << 1 / r << " b " << 1 / b << ")"; - /* - * Not entirely sure how to handle the fine search yet. Mostly the - * estimated CT is already good enough, but the fine search allows us to - * wander transverely off the CT curve. Under some illuminants, where - * there may be more or less green light, this may prove beneficial, - * though I probably need more real datasets before deciding exactly how - * this should be controlled and tuned. - */ - fineSearch(t, r, b, prior); - LOG(RPiAwb, Debug) - << "After fine search: r " << r << " b " << b << " (gains r " - << 1 / r << " b " << 1 / b << ")"; - /* - * Write results out for the main thread to pick up. Remember to adjust - * the gains from the ones that the "canonical sensor" would require to - * the ones needed by *this* sensor. - */ - asyncResults_.temperatureK = t; - asyncResults_.gainR = 1.0 / r * config_.sensitivityR; - asyncResults_.gainG = 1.0; - asyncResults_.gainB = 1.0 / b * config_.sensitivityB; -} - -void Awb::awbGrey() -{ - LOG(RPiAwb, Debug) << "Grey world AWB"; - /* - * Make a separate list of the derivatives for each of red and blue, so - * that we can sort them to exclude the extreme gains. We could - * consider some variations, such as normalising all the zones first, or - * doing an L2 average etc. - */ - std::vector &derivsR(zones_); - std::vector derivsB(derivsR); - std::sort(derivsR.begin(), derivsR.end(), - [](RGB const &a, RGB const &b) { - return a.G * b.R < b.G * a.R; - }); - std::sort(derivsB.begin(), derivsB.end(), - [](RGB const &a, RGB const &b) { - return a.G * b.B < b.G * a.B; - }); - /* Average the middle half of the values. */ - int discard = derivsR.size() / 4; - RGB sumR(0, 0, 0), sumB(0, 0, 0); - for (auto ri = derivsR.begin() + discard, - bi = derivsB.begin() + discard; - ri != derivsR.end() - discard; ri++, bi++) - sumR += *ri, sumB += *bi; - double gainR = sumR.G / (sumR.R + 1), - gainB = sumB.G / (sumB.B + 1); - asyncResults_.temperatureK = 4500; /* don't know what it is */ - asyncResults_.gainR = gainR; - asyncResults_.gainG = 1.0; - asyncResults_.gainB = gainB; -} - -void Awb::doAwb() -{ - prepareStats(); - LOG(RPiAwb, Debug) << "Valid zones: " << zones_.size(); - if (zones_.size() > config_.minRegions) { - if (config_.bayes) - awbBayes(); - else - awbGrey(); - LOG(RPiAwb, Debug) - << "CT found is " - << asyncResults_.temperatureK - << " with gains r " << asyncResults_.gainR - << " and b " << asyncResults_.gainB; - } - /* - * we're done with these; we may as well relinquish our hold on the - * pointer. - */ - statistics_.reset(); -} - -/* Register algorithm with the system. */ -static Algorithm *create(Controller *controller) -{ - return (Algorithm *)new Awb(controller); -} -static RegisterAlgorithm reg(NAME, &create); diff --git a/src/ipa/raspberrypi/controller/rpi/awb.h b/src/ipa/raspberrypi/controller/rpi/awb.h deleted file mode 100644 index e7d49cd8..00000000 --- a/src/ipa/raspberrypi/controller/rpi/awb.h +++ /dev/null @@ -1,191 +0,0 @@ -/* SPDX-License-Identifier: BSD-2-Clause */ -/* - * Copyright (C) 2019, Raspberry Pi Ltd - * - * awb.h - AWB control algorithm - */ -#pragma once - -#include -#include -#include - -#include "../awb_algorithm.h" -#include "../pwl.h" -#include "../awb_status.h" -#include "../statistics.h" - -namespace RPiController { - -/* Control algorithm to perform AWB calculations. */ - -struct AwbMode { - int read(const libcamera::YamlObject ¶ms); - double ctLo; /* low CT value for search */ - double ctHi; /* high CT value for search */ -}; - -struct AwbPrior { - int read(const libcamera::YamlObject ¶ms); - double lux; /* lux level */ - Pwl prior; /* maps CT to prior log likelihood for this lux level */ -}; - -struct AwbConfig { - AwbConfig() : defaultMode(nullptr) {} - int read(const libcamera::YamlObject ¶ms); - /* Only repeat the AWB calculation every "this many" frames */ - uint16_t framePeriod; - /* number of initial frames for which speed taken as 1.0 (maximum) */ - uint16_t startupFrames; - unsigned int convergenceFrames; /* approx number of frames to converge */ - double speed; /* IIR filter speed applied to algorithm results */ - bool fast; /* "fast" mode uses a 16x16 rather than 32x32 grid */ - Pwl ctR; /* function maps CT to r (= R/G) */ - Pwl ctB; /* function maps CT to b (= B/G) */ - Pwl ctRInverse; /* inverse of ctR */ - Pwl ctBInverse; /* inverse of ctB */ - /* table of illuminant priors at different lux levels */ - std::vector priors; - /* AWB "modes" (determines the search range) */ - std::map modes; - AwbMode *defaultMode; /* mode used if no mode selected */ - /* - * minimum proportion of pixels counted within AWB region for it to be - * "useful" - */ - double minPixels; - /* minimum G value of those pixels, to be regarded a "useful" */ - uint16_t minG; - /* - * number of AWB regions that must be "useful" in order to do the AWB - * calculation - */ - uint32_t minRegions; - /* clamp on colour error term (so as not to penalise non-grey excessively) */ - double deltaLimit; - /* step size control in coarse search */ - double coarseStep; - /* how far to wander off CT curve towards "more purple" */ - double transversePos; - /* how far to wander off CT curve towards "more green" */ - double transverseNeg; - /* - * red sensitivity ratio (set to canonical sensor's R/G divided by this - * sensor's R/G) - */ - double sensitivityR; - /* - * blue sensitivity ratio (set to canonical sensor's B/G divided by this - * sensor's B/G) - */ - double sensitivityB; - /* The whitepoint (which we normally "aim" for) can be moved. */ - double whitepointR; - double whitepointB; - bool bayes; /* use Bayesian algorithm */ -}; - -class Awb : public AwbAlgorithm -{ -public: - Awb(Controller *controller = NULL); - ~Awb(); - char const *name() const override; - void initialise() override; - int read(const libcamera::YamlObject ¶ms) override; - unsigned int getConvergenceFrames() const override; - void setMode(std::string const &name) override; - void setManualGains(double manualR, double manualB) override; - void enableAuto() override; - void disableAuto() override; - void switchMode(CameraMode const &cameraMode, Metadata *metadata) override; - void prepare(Metadata *imageMetadata) override; - void process(StatisticsPtr &stats, Metadata *imageMetadata) override; - struct RGB { - RGB(double r = 0, double g = 0, double b = 0) - : R(r), G(g), B(b) - { - } - double R, G, B; - RGB &operator+=(RGB const &other) - { - R += other.R, G += other.G, B += other.B; - return *this; - } - }; - -private: - bool isAutoEnabled() const; - /* configuration is read-only, and available to both threads */ - AwbConfig config_; - std::thread asyncThread_; - void asyncFunc(); /* asynchronous thread function */ - std::mutex mutex_; - /* condvar for async thread to wait on */ - std::condition_variable asyncSignal_; - /* condvar for synchronous thread to wait on */ - std::condition_variable syncSignal_; - /* for sync thread to check if async thread finished (requires mutex) */ - bool asyncFinished_; - /* for async thread to check if it's been told to run (requires mutex) */ - bool asyncStart_; - /* for async thread to check if it's been told to quit (requires mutex) */ - bool asyncAbort_; - - /* - * The following are only for the synchronous thread to use: - * for sync thread to note its has asked async thread to run - */ - bool asyncStarted_; - /* counts up to framePeriod before restarting the async thread */ - int framePhase_; - int frameCount_; /* counts up to startup_frames */ - AwbStatus syncResults_; - AwbStatus prevSyncResults_; - std::string modeName_; - /* - * The following are for the asynchronous thread to use, though the main - * thread can set/reset them if the async thread is known to be idle: - */ - void restartAsync(StatisticsPtr &stats, double lux); - /* copy out the results from the async thread so that it can be restarted */ - void fetchAsyncResults(); - StatisticsPtr statistics_; - AwbMode *mode_; - double lux_; - AwbStatus asyncResults_; - void doAwb(); - void awbBayes(); - void awbGrey(); - void prepareStats(); - double computeDelta2Sum(double gainR, double gainB); - Pwl interpolatePrior(); - double coarseSearch(Pwl const &prior); - void fineSearch(double &t, double &r, double &b, Pwl const &prior); - std::vector zones_; - std::vector points_; - /* manual r setting */ - double manualR_; - /* manual b setting */ - double manualB_; -}; - -static inline Awb::RGB operator+(Awb::RGB const &a, Awb::RGB const &b) -{ - return Awb::RGB(a.R + b.R, a.G + b.G, a.B + b.B); -} -static inline Awb::RGB operator-(Awb::RGB const &a, Awb::RGB const &b) -{ - return Awb::RGB(a.R - b.R, a.G - b.G, a.B - b.B); -} -static inline Awb::RGB operator*(double d, Awb::RGB const &rgb) -{ - return Awb::RGB(d * rgb.R, d * rgb.G, d * rgb.B); -} -static inline Awb::RGB operator*(Awb::RGB const &rgb, double d) -{ - return d * rgb; -} - -} /* namespace RPiController */ diff --git a/src/ipa/raspberrypi/controller/rpi/black_level.cpp b/src/ipa/raspberrypi/controller/rpi/black_level.cpp deleted file mode 100644 index 85baec3f..00000000 --- a/src/ipa/raspberrypi/controller/rpi/black_level.cpp +++ /dev/null @@ -1,66 +0,0 @@ -/* SPDX-License-Identifier: BSD-2-Clause */ -/* - * Copyright (C) 2019, Raspberry Pi Ltd - * - * black_level.cpp - black level control algorithm - */ - -#include -#include - -#include - -#include "../black_level_status.h" - -#include "black_level.h" - -using namespace RPiController; -using namespace libcamera; - -LOG_DEFINE_CATEGORY(RPiBlackLevel) - -#define NAME "rpi.black_level" - -BlackLevel::BlackLevel(Controller *controller) - : Algorithm(controller) -{ -} - -char const *BlackLevel::name() const -{ - return NAME; -} - -int BlackLevel::read(const libcamera::YamlObject ¶ms) -{ - /* 64 in 10 bits scaled to 16 bits */ - uint16_t blackLevel = params["black_level"].get(4096); - blackLevelR_ = params["black_level_r"].get(blackLevel); - blackLevelG_ = params["black_level_g"].get(blackLevel); - blackLevelB_ = params["black_level_b"].get(blackLevel); - LOG(RPiBlackLevel, Debug) - << " Read black levels red " << blackLevelR_ - << " green " << blackLevelG_ - << " blue " << blackLevelB_; - return 0; -} - -void BlackLevel::prepare(Metadata *imageMetadata) -{ - /* - * Possibly we should think about doing this in a switchMode or - * something? - */ - struct BlackLevelStatus status; - status.blackLevelR = blackLevelR_; - status.blackLevelG = blackLevelG_; - status.blackLevelB = blackLevelB_; - imageMetadata->set("black_level.status", status); -} - -/* Register algorithm with the system. */ -static Algorithm *create(Controller *controller) -{ - return new BlackLevel(controller); -} -static RegisterAlgorithm reg(NAME, &create); diff --git a/src/ipa/raspberrypi/controller/rpi/black_level.h b/src/ipa/raspberrypi/controller/rpi/black_level.h deleted file mode 100644 index 2403f7f7..00000000 --- a/src/ipa/raspberrypi/controller/rpi/black_level.h +++ /dev/null @@ -1,30 +0,0 @@ -/* SPDX-License-Identifier: BSD-2-Clause */ -/* - * Copyright (C) 2019, Raspberry Pi Ltd - * - * black_level.h - black level control algorithm - */ -#pragma once - -#include "../algorithm.h" -#include "../black_level_status.h" - -/* This is our implementation of the "black level algorithm". */ - -namespace RPiController { - -class BlackLevel : public Algorithm -{ -public: - BlackLevel(Controller *controller); - char const *name() const override; - int read(const libcamera::YamlObject ¶ms) override; - void prepare(Metadata *imageMetadata) override; - -private: - double blackLevelR_; - double blackLevelG_; - double blackLevelB_; -}; - -} /* namespace RPiController */ diff --git a/src/ipa/raspberrypi/controller/rpi/ccm.cpp b/src/ipa/raspberrypi/controller/rpi/ccm.cpp deleted file mode 100644 index 2e2e6664..00000000 --- a/src/ipa/raspberrypi/controller/rpi/ccm.cpp +++ /dev/null @@ -1,199 +0,0 @@ -/* SPDX-License-Identifier: BSD-2-Clause */ -/* - * Copyright (C) 2019, Raspberry Pi Ltd - * - * ccm.cpp - CCM (colour correction matrix) control algorithm - */ - -#include - -#include "../awb_status.h" -#include "../ccm_status.h" -#include "../lux_status.h" -#include "../metadata.h" - -#include "ccm.h" - -using namespace RPiController; -using namespace libcamera; - -LOG_DEFINE_CATEGORY(RPiCcm) - -/* - * This algorithm selects a CCM (Colour Correction Matrix) according to the - * colour temperature estimated by AWB (interpolating between known matricies as - * necessary). Additionally the amount of colour saturation can be controlled - * both according to the current estimated lux level and according to a - * saturation setting that is exposed to applications. - */ - -#define NAME "rpi.ccm" - -Matrix::Matrix() -{ - memset(m, 0, sizeof(m)); -} -Matrix::Matrix(double m0, double m1, double m2, double m3, double m4, double m5, - double m6, double m7, double m8) -{ - m[0][0] = m0, m[0][1] = m1, m[0][2] = m2, m[1][0] = m3, m[1][1] = m4, - m[1][2] = m5, m[2][0] = m6, m[2][1] = m7, m[2][2] = m8; -} -int Matrix::read(const libcamera::YamlObject ¶ms) -{ - double *ptr = (double *)m; - - if (params.size() != 9) { - LOG(RPiCcm, Error) << "Wrong number of values in CCM"; - return -EINVAL; - } - - for (const auto ¶m : params.asList()) { - auto value = param.get(); - if (!value) - return -EINVAL; - *ptr++ = *value; - } - - return 0; -} - -Ccm::Ccm(Controller *controller) - : CcmAlgorithm(controller), saturation_(1.0) {} - -char const *Ccm::name() const -{ - return NAME; -} - -int Ccm::read(const libcamera::YamlObject ¶ms) -{ - int ret; - - if (params.contains("saturation")) { - ret = config_.saturation.read(params["saturation"]); - if (ret) - return ret; - } - - for (auto &p : params["ccms"].asList()) { - auto value = p["ct"].get(); - if (!value) - return -EINVAL; - - CtCcm ctCcm; - ctCcm.ct = *value; - ret = ctCcm.ccm.read(p["ccm"]); - if (ret) - return ret; - - if (!config_.ccms.empty() && ctCcm.ct <= config_.ccms.back().ct) { - LOG(RPiCcm, Error) - << "CCM not in increasing colour temperature order"; - return -EINVAL; - } - - config_.ccms.push_back(std::move(ctCcm)); - } - - if (config_.ccms.empty()) { - LOG(RPiCcm, Error) << "No CCMs specified"; - return -EINVAL; - } - - return 0; -} - -void Ccm::setSaturation(double saturation) -{ - saturation_ = saturation; -} - -void Ccm::initialise() -{ -} - -template -static bool getLocked(Metadata *metadata, std::string const &tag, T &value) -{ - T *ptr = metadata->getLocked(tag); - if (ptr == nullptr) - return false; - value = *ptr; - return true; -} - -Matrix calculateCcm(std::vector const &ccms, double ct) -{ - if (ct <= ccms.front().ct) - return ccms.front().ccm; - else if (ct >= ccms.back().ct) - return ccms.back().ccm; - else { - int i = 0; - for (; ct > ccms[i].ct; i++) - ; - double lambda = - (ct - ccms[i - 1].ct) / (ccms[i].ct - ccms[i - 1].ct); - return lambda * ccms[i].ccm + (1.0 - lambda) * ccms[i - 1].ccm; - } -} - -Matrix applySaturation(Matrix const &ccm, double saturation) -{ - Matrix RGB2Y(0.299, 0.587, 0.114, -0.169, -0.331, 0.500, 0.500, -0.419, - -0.081); - Matrix Y2RGB(1.000, 0.000, 1.402, 1.000, -0.345, -0.714, 1.000, 1.771, - 0.000); - Matrix S(1, 0, 0, 0, saturation, 0, 0, 0, saturation); - return Y2RGB * S * RGB2Y * ccm; -} - -void Ccm::prepare(Metadata *imageMetadata) -{ - bool awbOk = false, luxOk = false; - struct AwbStatus awb = {}; - awb.temperatureK = 4000; /* in case no metadata */ - struct LuxStatus lux = {}; - lux.lux = 400; /* in case no metadata */ - { - /* grab mutex just once to get everything */ - std::lock_guard lock(*imageMetadata); - awbOk = getLocked(imageMetadata, "awb.status", awb); - luxOk = getLocked(imageMetadata, "lux.status", lux); - } - if (!awbOk) - LOG(RPiCcm, Warning) << "no colour temperature found"; - if (!luxOk) - LOG(RPiCcm, Warning) << "no lux value found"; - Matrix ccm = calculateCcm(config_.ccms, awb.temperatureK); - double saturation = saturation_; - struct CcmStatus ccmStatus; - ccmStatus.saturation = saturation; - if (!config_.saturation.empty()) - saturation *= config_.saturation.eval( - config_.saturation.domain().clip(lux.lux)); - ccm = applySaturation(ccm, saturation); - for (int j = 0; j < 3; j++) - for (int i = 0; i < 3; i++) - ccmStatus.matrix[j * 3 + i] = - std::max(-8.0, std::min(7.9999, ccm.m[j][i])); - LOG(RPiCcm, Debug) - << "colour temperature " << awb.temperatureK << "K"; - LOG(RPiCcm, Debug) - << "CCM: " << ccmStatus.matrix[0] << " " << ccmStatus.matrix[1] - << " " << ccmStatus.matrix[2] << " " - << ccmStatus.matrix[3] << " " << ccmStatus.matrix[4] - << " " << ccmStatus.matrix[5] << " " - << ccmStatus.matrix[6] << " " << ccmStatus.matrix[7] - << " " << ccmStatus.matrix[8]; - imageMetadata->set("ccm.status", ccmStatus); -} - -/* Register algorithm with the system. */ -static Algorithm *create(Controller *controller) -{ - return (Algorithm *)new Ccm(controller); - ; -} -static RegisterAlgorithm reg(NAME, &create); diff --git a/src/ipa/raspberrypi/controller/rpi/ccm.h b/src/ipa/raspberrypi/controller/rpi/ccm.h deleted file mode 100644 index 286d0b33..00000000 --- a/src/ipa/raspberrypi/controller/rpi/ccm.h +++ /dev/null @@ -1,75 +0,0 @@ -/* SPDX-License-Identifier: BSD-2-Clause */ -/* - * Copyright (C) 2019, Raspberry Pi Ltd - * - * ccm.h - CCM (colour correction matrix) control algorithm - */ -#pragma once - -#include - -#include "../ccm_algorithm.h" -#include "../pwl.h" - -namespace RPiController { - -/* Algorithm to calculate colour matrix. Should be placed after AWB. */ - -struct Matrix { - Matrix(double m0, double m1, double m2, double m3, double m4, double m5, - double m6, double m7, double m8); - Matrix(); - double m[3][3]; - int read(const libcamera::YamlObject ¶ms); -}; -static inline Matrix operator*(double d, Matrix const &m) -{ - return Matrix(m.m[0][0] * d, m.m[0][1] * d, m.m[0][2] * d, - m.m[1][0] * d, m.m[1][1] * d, m.m[1][2] * d, - m.m[2][0] * d, m.m[2][1] * d, m.m[2][2] * d); -} -static inline Matrix operator*(Matrix const &m1, Matrix const &m2) -{ - Matrix m; - for (int i = 0; i < 3; i++) - for (int j = 0; j < 3; j++) - m.m[i][j] = m1.m[i][0] * m2.m[0][j] + - m1.m[i][1] * m2.m[1][j] + - m1.m[i][2] * m2.m[2][j]; - return m; -} -static inline Matrix operator+(Matrix const &m1, Matrix const &m2) -{ - Matrix m; - for (int i = 0; i < 3; i++) - for (int j = 0; j < 3; j++) - m.m[i][j] = m1.m[i][j] + m2.m[i][j]; - return m; -} - -struct CtCcm { - double ct; - Matrix ccm; -}; - -struct CcmConfig { - std::vector ccms; - Pwl saturation; -}; - -class Ccm : public CcmAlgorithm -{ -public: - Ccm(Controller *controller = NULL); - char const *name() const override; - int read(const libcamera::YamlObject ¶ms) override; - void setSaturation(double saturation) override; - void initialise() override; - void prepare(Metadata *imageMetadata) override; - -private: - CcmConfig config_; - double saturation_; -}; - -} /* namespace RPiController */ diff --git a/src/ipa/raspberrypi/controller/rpi/contrast.cpp b/src/ipa/raspberrypi/controller/rpi/contrast.cpp deleted file mode 100644 index bee1eadd..00000000 --- a/src/ipa/raspberrypi/controller/rpi/contrast.cpp +++ /dev/null @@ -1,181 +0,0 @@ -/* SPDX-License-Identifier: BSD-2-Clause */ -/* - * Copyright (C) 2019, Raspberry Pi Ltd - * - * contrast.cpp - contrast (gamma) control algorithm - */ -#include - -#include - -#include "../contrast_status.h" -#include "../histogram.h" - -#include "contrast.h" - -using namespace RPiController; -using namespace libcamera; - -LOG_DEFINE_CATEGORY(RPiContrast) - -/* - * This is a very simple control algorithm which simply retrieves the results of - * AGC and AWB via their "status" metadata, and applies digital gain to the - * colour channels in accordance with those instructions. We take care never to - * apply less than unity gains, as that would cause fully saturated pixels to go - * off-white. - */ - -#define NAME "rpi.contrast" - -Contrast::Contrast(Controller *controller) - : ContrastAlgorithm(controller), brightness_(0.0), contrast_(1.0) -{ -} - -char const *Contrast::name() const -{ - return NAME; -} - -int Contrast::read(const libcamera::YamlObject ¶ms) -{ - // enable adaptive enhancement by default - config_.ceEnable = params["ce_enable"].get(1); - // the point near the bottom of the histogram to move - config_.loHistogram = params["lo_histogram"].get(0.01); - // where in the range to try and move it to - config_.loLevel = params["lo_level"].get(0.015); - // but don't move by more than this - config_.loMax = params["lo_max"].get(500); - // equivalent values for the top of the histogram... - config_.hiHistogram = params["hi_histogram"].get(0.95); - config_.hiLevel = params["hi_level"].get(0.95); - config_.hiMax = params["hi_max"].get(2000); - return config_.gammaCurve.read(params["gamma_curve"]); -} - -void Contrast::setBrightness(double brightness) -{ - brightness_ = brightness; -} - -void Contrast::setContrast(double contrast) -{ - contrast_ = contrast; -} - -void Contrast::initialise() -{ - /* - * Fill in some default values as Prepare will run before Process gets - * called. - */ - status_.brightness = brightness_; - status_.contrast = contrast_; - status_.gammaCurve = config_.gammaCurve; -} - -void Contrast::prepare(Metadata *imageMetadata) -{ - imageMetadata->set("contrast.status", status_); -} - -Pwl computeStretchCurve(Histogram const &histogram, - ContrastConfig const &config) -{ - Pwl enhance; - enhance.append(0, 0); - /* - * If the start of the histogram is rather empty, try to pull it down a - * bit. - */ - double histLo = histogram.quantile(config.loHistogram) * - (65536 / histogram.bins()); - double levelLo = config.loLevel * 65536; - LOG(RPiContrast, Debug) - << "Move histogram point " << histLo << " to " << levelLo; - histLo = std::max(levelLo, - std::min(65535.0, std::min(histLo, levelLo + config.loMax))); - LOG(RPiContrast, Debug) - << "Final values " << histLo << " -> " << levelLo; - enhance.append(histLo, levelLo); - /* - * Keep the mid-point (median) in the same place, though, to limit the - * apparent amount of global brightness shift. - */ - double mid = histogram.quantile(0.5) * (65536 / histogram.bins()); - enhance.append(mid, mid); - - /* - * If the top to the histogram is empty, try to pull the pixel values - * there up. - */ - double histHi = histogram.quantile(config.hiHistogram) * - (65536 / histogram.bins()); - double levelHi = config.hiLevel * 65536; - LOG(RPiContrast, Debug) - << "Move histogram point " << histHi << " to " << levelHi; - histHi = std::min(levelHi, - std::max(0.0, std::max(histHi, levelHi - config.hiMax))); - LOG(RPiContrast, Debug) - << "Final values " << histHi << " -> " << levelHi; - enhance.append(histHi, levelHi); - enhance.append(65535, 65535); - return enhance; -} - -Pwl applyManualContrast(Pwl const &gammaCurve, double brightness, - double contrast) -{ - Pwl newGammaCurve; - LOG(RPiContrast, Debug) - << "Manual brightness " << brightness << " contrast " << contrast; - gammaCurve.map([&](double x, double y) { - newGammaCurve.append( - x, std::max(0.0, std::min(65535.0, - (y - 32768) * contrast + - 32768 + brightness))); - }); - return newGammaCurve; -} - -void Contrast::process(StatisticsPtr &stats, - [[maybe_unused]] Metadata *imageMetadata) -{ - Histogram &histogram = stats->yHist; - /* - * We look at the histogram and adjust the gamma curve in the following - * ways: 1. Adjust the gamma curve so as to pull the start of the - * histogram down, and possibly push the end up. - */ - Pwl gammaCurve = config_.gammaCurve; - if (config_.ceEnable) { - if (config_.loMax != 0 || config_.hiMax != 0) - gammaCurve = computeStretchCurve(histogram, config_).compose(gammaCurve); - /* - * We could apply other adjustments (e.g. partial equalisation) - * based on the histogram...? - */ - } - /* - * 2. Finally apply any manually selected brightness/contrast - * adjustment. - */ - if (brightness_ != 0 || contrast_ != 1.0) - gammaCurve = applyManualContrast(gammaCurve, brightness_, contrast_); - /* - * And fill in the status for output. Use more points towards the bottom - * of the curve. - */ - status_.brightness = brightness_; - status_.contrast = contrast_; - status_.gammaCurve = std::move(gammaCurve); -} - -/* Register algorithm with the system. */ -static Algorithm *create(Controller *controller) -{ - return (Algorithm *)new Contrast(controller); -} -static RegisterAlgorithm reg(NAME, &create); diff --git a/src/ipa/raspberrypi/controller/rpi/contrast.h b/src/ipa/raspberrypi/controller/rpi/contrast.h deleted file mode 100644 index 9c81277a..00000000 --- a/src/ipa/raspberrypi/controller/rpi/contrast.h +++ /dev/null @@ -1,51 +0,0 @@ -/* SPDX-License-Identifier: BSD-2-Clause */ -/* - * Copyright (C) 2019, Raspberry Pi Ltd - * - * contrast.h - contrast (gamma) control algorithm - */ -#pragma once - -#include - -#include "../contrast_algorithm.h" -#include "../pwl.h" - -namespace RPiController { - -/* - * Back End algorithm to appaly correct digital gain. Should be placed after - * Back End AWB. - */ - -struct ContrastConfig { - bool ceEnable; - double loHistogram; - double loLevel; - double loMax; - double hiHistogram; - double hiLevel; - double hiMax; - Pwl gammaCurve; -}; - -class Contrast : public ContrastAlgorithm -{ -public: - Contrast(Controller *controller = NULL); - char const *name() const override; - int read(const libcamera::YamlObject ¶ms) override; - void setBrightness(double brightness) override; - void setContrast(double contrast) override; - void initialise() override; - void prepare(Metadata *imageMetadata) override; - void process(StatisticsPtr &stats, Metadata *imageMetadata) override; - -private: - ContrastConfig config_; - double brightness_; - double contrast_; - ContrastStatus status_; -}; - -} /* namespace RPiController */ diff --git a/src/ipa/raspberrypi/controller/rpi/dpc.cpp b/src/ipa/raspberrypi/controller/rpi/dpc.cpp deleted file mode 100644 index be3871df..00000000 --- a/src/ipa/raspberrypi/controller/rpi/dpc.cpp +++ /dev/null @@ -1,59 +0,0 @@ -/* SPDX-License-Identifier: BSD-2-Clause */ -/* - * Copyright (C) 2019, Raspberry Pi Ltd - * - * dpc.cpp - DPC (defective pixel correction) control algorithm - */ - -#include - -#include "dpc.h" - -using namespace RPiController; -using namespace libcamera; - -LOG_DEFINE_CATEGORY(RPiDpc) - -/* - * We use the lux status so that we can apply stronger settings in darkness (if - * necessary). - */ - -#define NAME "rpi.dpc" - -Dpc::Dpc(Controller *controller) - : Algorithm(controller) -{ -} - -char const *Dpc::name() const -{ - return NAME; -} - -int Dpc::read(const libcamera::YamlObject ¶ms) -{ - config_.strength = params["strength"].get(1); - if (config_.strength < 0 || config_.strength > 2) { - LOG(RPiDpc, Error) << "Bad strength value"; - return -EINVAL; - } - - return 0; -} - -void Dpc::prepare(Metadata *imageMetadata) -{ - DpcStatus dpcStatus = {}; - /* Should we vary this with lux level or analogue gain? TBD. */ - dpcStatus.strength = config_.strength; - LOG(RPiDpc, Debug) << "strength " << dpcStatus.strength; - imageMetadata->set("dpc.status", dpcStatus); -} - -/* Register algorithm with the system. */ -static Algorithm *create(Controller *controller) -{ - return (Algorithm *)new Dpc(controller); -} -static RegisterAlgorithm reg(NAME, &create); diff --git a/src/ipa/raspberrypi/controller/rpi/dpc.h b/src/ipa/raspberrypi/controller/rpi/dpc.h deleted file mode 100644 index 84a05604..00000000 --- a/src/ipa/raspberrypi/controller/rpi/dpc.h +++ /dev/null @@ -1,32 +0,0 @@ -/* SPDX-License-Identifier: BSD-2-Clause */ -/* - * Copyright (C) 2019, Raspberry Pi Ltd - * - * dpc.h - DPC (defective pixel correction) control algorithm - */ -#pragma once - -#include "../algorithm.h" -#include "../dpc_status.h" - -namespace RPiController { - -/* Back End algorithm to apply appropriate GEQ settings. */ - -struct DpcConfig { - int strength; -}; - -class Dpc : public Algorithm -{ -public: - Dpc(Controller *controller); - char const *name() const override; - int read(const libcamera::YamlObject ¶ms) override; - void prepare(Metadata *imageMetadata) override; - -private: - DpcConfig config_; -}; - -} /* namespace RPiController */ diff --git a/src/ipa/raspberrypi/controller/rpi/focus.h b/src/ipa/raspberrypi/controller/rpi/focus.h deleted file mode 100644 index 8556039d..00000000 --- a/src/ipa/raspberrypi/controller/rpi/focus.h +++ /dev/null @@ -1,28 +0,0 @@ -/* SPDX-License-Identifier: BSD-2-Clause */ -/* - * Copyright (C) 2020, Raspberry Pi Ltd - * - * focus.h - focus algorithm - */ -#pragma once - -#include "../algorithm.h" -#include "../metadata.h" - -/* - * The "focus" algorithm. All it does it print out a version of the - * focus contrast measure; there is no actual auto-focus mechanism to - * control. - */ - -namespace RPiController { - -class Focus : public Algorithm -{ -public: - Focus(Controller *controller); - char const *name() const override; - void process(StatisticsPtr &stats, Metadata *imageMetadata) override; -}; - -} /* namespace RPiController */ diff --git a/src/ipa/raspberrypi/controller/rpi/geq.cpp b/src/ipa/raspberrypi/controller/rpi/geq.cpp deleted file mode 100644 index 510870e9..00000000 --- a/src/ipa/raspberrypi/controller/rpi/geq.cpp +++ /dev/null @@ -1,89 +0,0 @@ -/* SPDX-License-Identifier: BSD-2-Clause */ -/* - * Copyright (C) 2019, Raspberry Pi Ltd - * - * geq.cpp - GEQ (green equalisation) control algorithm - */ - -#include - -#include "../device_status.h" -#include "../lux_status.h" -#include "../pwl.h" - -#include "geq.h" - -using namespace RPiController; -using namespace libcamera; - -LOG_DEFINE_CATEGORY(RPiGeq) - -/* - * We use the lux status so that we can apply stronger settings in darkness (if - * necessary). - */ - -#define NAME "rpi.geq" - -Geq::Geq(Controller *controller) - : Algorithm(controller) -{ -} - -char const *Geq::name() const -{ - return NAME; -} - -int Geq::read(const libcamera::YamlObject ¶ms) -{ - config_.offset = params["offset"].get(0); - config_.slope = params["slope"].get(0.0); - if (config_.slope < 0.0 || config_.slope >= 1.0) { - LOG(RPiGeq, Error) << "Bad slope value"; - return -EINVAL; - } - - if (params.contains("strength")) { - int ret = config_.strength.read(params["strength"]); - if (ret) - return ret; - } - - return 0; -} - -void Geq::prepare(Metadata *imageMetadata) -{ - LuxStatus luxStatus = {}; - luxStatus.lux = 400; - if (imageMetadata->get("lux.status", luxStatus)) - LOG(RPiGeq, Warning) << "no lux data found"; - DeviceStatus deviceStatus; - deviceStatus.analogueGain = 1.0; /* in case not found */ - if (imageMetadata->get("device.status", deviceStatus)) - LOG(RPiGeq, Warning) - << "no device metadata - use analogue gain of 1x"; - GeqStatus geqStatus = {}; - double strength = config_.strength.empty() - ? 1.0 - : config_.strength.eval(config_.strength.domain().clip(luxStatus.lux)); - strength *= deviceStatus.analogueGain; - double offset = config_.offset * strength; - double slope = config_.slope * strength; - geqStatus.offset = std::min(65535.0, std::max(0.0, offset)); - geqStatus.slope = std::min(.99999, std::max(0.0, slope)); - LOG(RPiGeq, Debug) - << "offset " << geqStatus.offset << " slope " - << geqStatus.slope << " (analogue gain " - << deviceStatus.analogueGain << " lux " - << luxStatus.lux << ")"; - imageMetadata->set("geq.status", geqStatus); -} - -/* Register algorithm with the system. */ -static Algorithm *create(Controller *controller) -{ - return (Algorithm *)new Geq(controller); -} -static RegisterAlgorithm reg(NAME, &create); diff --git a/src/ipa/raspberrypi/controller/rpi/geq.h b/src/ipa/raspberrypi/controller/rpi/geq.h deleted file mode 100644 index ee3a52ff..00000000 --- a/src/ipa/raspberrypi/controller/rpi/geq.h +++ /dev/null @@ -1,34 +0,0 @@ -/* SPDX-License-Identifier: BSD-2-Clause */ -/* - * Copyright (C) 2019, Raspberry Pi Ltd - * - * geq.h - GEQ (green equalisation) control algorithm - */ -#pragma once - -#include "../algorithm.h" -#include "../geq_status.h" - -namespace RPiController { - -/* Back End algorithm to apply appropriate GEQ settings. */ - -struct GeqConfig { - uint16_t offset; - double slope; - Pwl strength; /* lux to strength factor */ -}; - -class Geq : public Algorithm -{ -public: - Geq(Controller *controller); - char const *name() const override; - int read(const libcamera::YamlObject ¶ms) override; - void prepare(Metadata *imageMetadata) override; - -private: - GeqConfig config_; -}; - -} /* namespace RPiController */ diff --git a/src/ipa/raspberrypi/controller/rpi/lux.cpp b/src/ipa/raspberrypi/controller/rpi/lux.cpp deleted file mode 100644 index 06625f3a..00000000 --- a/src/ipa/raspberrypi/controller/rpi/lux.cpp +++ /dev/null @@ -1,115 +0,0 @@ -/* SPDX-License-Identifier: BSD-2-Clause */ -/* - * Copyright (C) 2019, Raspberry Pi Ltd - * - * lux.cpp - Lux control algorithm - */ -#include - -#include - -#include "../device_status.h" - -#include "lux.h" - -using namespace RPiController; -using namespace libcamera; -using namespace std::literals::chrono_literals; - -LOG_DEFINE_CATEGORY(RPiLux) - -#define NAME "rpi.lux" - -Lux::Lux(Controller *controller) - : Algorithm(controller) -{ - /* - * Put in some defaults as there will be no meaningful values until - * Process has run. - */ - status_.aperture = 1.0; - status_.lux = 400; -} - -char const *Lux::name() const -{ - return NAME; -} - -int Lux::read(const libcamera::YamlObject ¶ms) -{ - auto value = params["reference_shutter_speed"].get(); - if (!value) - return -EINVAL; - referenceShutterSpeed_ = *value * 1.0us; - - value = params["reference_gain"].get(); - if (!value) - return -EINVAL; - referenceGain_ = *value; - - referenceAperture_ = params["reference_aperture"].get(1.0); - - value = params["reference_Y"].get(); - if (!value) - return -EINVAL; - referenceY_ = *value; - - value = params["reference_lux"].get(); - if (!value) - return -EINVAL; - referenceLux_ = *value; - - currentAperture_ = referenceAperture_; - return 0; -} - -void Lux::setCurrentAperture(double aperture) -{ - currentAperture_ = aperture; -} - -void Lux::prepare(Metadata *imageMetadata) -{ - std::unique_lock lock(mutex_); - imageMetadata->set("lux.status", status_); -} - -void Lux::process(StatisticsPtr &stats, Metadata *imageMetadata) -{ - DeviceStatus deviceStatus; - if (imageMetadata->get("device.status", deviceStatus) == 0) { - double currentGain = deviceStatus.analogueGain; - double currentAperture = deviceStatus.aperture.value_or(currentAperture_); - double currentY = stats->yHist.interQuantileMean(0, 1); - double gainRatio = referenceGain_ / currentGain; - double shutterSpeedRatio = - referenceShutterSpeed_ / deviceStatus.shutterSpeed; - double apertureRatio = referenceAperture_ / currentAperture; - double yRatio = currentY * (65536 / stats->yHist.bins()) / referenceY_; - double estimatedLux = shutterSpeedRatio * gainRatio * - apertureRatio * apertureRatio * - yRatio * referenceLux_; - LuxStatus status; - status.lux = estimatedLux; - status.aperture = currentAperture; - LOG(RPiLux, Debug) << ": estimated lux " << estimatedLux; - { - std::unique_lock lock(mutex_); - status_ = status; - } - /* - * Overwrite the metadata here as well, so that downstream - * algorithms get the latest value. - */ - imageMetadata->set("lux.status", status); - } else - LOG(RPiLux, Warning) << ": no device metadata"; -} - -/* Register algorithm with the system. */ -static Algorithm *create(Controller *controller) -{ - return (Algorithm *)new Lux(controller); -} -static RegisterAlgorithm reg(NAME, &create); diff --git a/src/ipa/raspberrypi/controller/rpi/lux.h b/src/ipa/raspberrypi/controller/rpi/lux.h deleted file mode 100644 index 89411a54..00000000 --- a/src/ipa/raspberrypi/controller/rpi/lux.h +++ /dev/null @@ -1,45 +0,0 @@ -/* SPDX-License-Identifier: BSD-2-Clause */ -/* - * Copyright (C) 2019, Raspberry Pi Ltd - * - * lux.h - Lux control algorithm - */ -#pragma once - -#include - -#include - -#include "../lux_status.h" -#include "../algorithm.h" - -/* This is our implementation of the "lux control algorithm". */ - -namespace RPiController { - -class Lux : public Algorithm -{ -public: - Lux(Controller *controller); - char const *name() const override; - int read(const libcamera::YamlObject ¶ms) override; - void prepare(Metadata *imageMetadata) override; - void process(StatisticsPtr &stats, Metadata *imageMetadata) override; - void setCurrentAperture(double aperture); - -private: - /* - * These values define the conditions of the reference image, against - * which we compare the new image. - */ - libcamera::utils::Duration referenceShutterSpeed_; - double referenceGain_; - double referenceAperture_; /* units of 1/f */ - double referenceY_; /* out of 65536 */ - double referenceLux_; - double currentAperture_; - LuxStatus status_; - std::mutex mutex_; -}; - -} /* namespace RPiController */ diff --git a/src/ipa/raspberrypi/controller/rpi/noise.cpp b/src/ipa/raspberrypi/controller/rpi/noise.cpp deleted file mode 100644 index bcd8b9ed..00000000 --- a/src/ipa/raspberrypi/controller/rpi/noise.cpp +++ /dev/null @@ -1,89 +0,0 @@ -/* SPDX-License-Identifier: BSD-2-Clause */ -/* - * Copyright (C) 2019, Raspberry Pi Ltd - * - * noise.cpp - Noise control algorithm - */ - -#include - -#include - -#include "../device_status.h" -#include "../noise_status.h" - -#include "noise.h" - -using namespace RPiController; -using namespace libcamera; - -LOG_DEFINE_CATEGORY(RPiNoise) - -#define NAME "rpi.noise" - -Noise::Noise(Controller *controller) - : Algorithm(controller), modeFactor_(1.0) -{ -} - -char const *Noise::name() const -{ - return NAME; -} - -void Noise::switchMode(CameraMode const &cameraMode, - [[maybe_unused]] Metadata *metadata) -{ - /* - * For example, we would expect a 2x2 binned mode to have a "noise - * factor" of sqrt(2x2) = 2. (can't be less than one, right?) - */ - modeFactor_ = std::max(1.0, cameraMode.noiseFactor); -} - -int Noise::read(const libcamera::YamlObject ¶ms) -{ - auto value = params["reference_constant"].get(); - if (!value) - return -EINVAL; - referenceConstant_ = *value; - - value = params["reference_slope"].get(); - if (!value) - return -EINVAL; - referenceSlope_ = *value; - - return 0; -} - -void Noise::prepare(Metadata *imageMetadata) -{ - struct DeviceStatus deviceStatus; - deviceStatus.analogueGain = 1.0; /* keep compiler calm */ - if (imageMetadata->get("device.status", deviceStatus) == 0) { - /* - * There is a slight question as to exactly how the noise - * profile, specifically the constant part of it, scales. For - * now we assume it all scales the same, and we'll revisit this - * if it proves substantially wrong. NOTE: we may also want to - * make some adjustments based on the camera mode (such as - * binning), if we knew how to discover it... - */ - double factor = sqrt(deviceStatus.analogueGain) / modeFactor_; - struct NoiseStatus status; - status.noiseConstant = referenceConstant_ * factor; - status.noiseSlope = referenceSlope_ * factor; - imageMetadata->set("noise.status", status); - LOG(RPiNoise, Debug) - << "constant " << status.noiseConstant - << " slope " << status.noiseSlope; - } else - LOG(RPiNoise, Warning) << " no metadata"; -} - -/* Register algorithm with the system. */ -static Algorithm *create(Controller *controller) -{ - return new Noise(controller); -} -static RegisterAlgorithm reg(NAME, &create); diff --git a/src/ipa/raspberrypi/controller/rpi/noise.h b/src/ipa/raspberrypi/controller/rpi/noise.h deleted file mode 100644 index 74c31e64..00000000 --- a/src/ipa/raspberrypi/controller/rpi/noise.h +++ /dev/null @@ -1,32 +0,0 @@ -/* SPDX-License-Identifier: BSD-2-Clause */ -/* - * Copyright (C) 2019, Raspberry Pi Ltd - * - * noise.h - Noise control algorithm - */ -#pragma once - -#include "../algorithm.h" -#include "../noise_status.h" - -/* This is our implementation of the "noise algorithm". */ - -namespace RPiController { - -class Noise : public Algorithm -{ -public: - Noise(Controller *controller); - char const *name() const override; - void switchMode(CameraMode const &cameraMode, Metadata *metadata) override; - int read(const libcamera::YamlObject ¶ms) override; - void prepare(Metadata *imageMetadata) override; - -private: - /* the noise profile for analogue gain of 1.0 */ - double referenceConstant_; - double referenceSlope_; - double modeFactor_; -}; - -} /* namespace RPiController */ diff --git a/src/ipa/raspberrypi/controller/rpi/sdn.cpp b/src/ipa/raspberrypi/controller/rpi/sdn.cpp deleted file mode 100644 index b6b66251..00000000 --- a/src/ipa/raspberrypi/controller/rpi/sdn.cpp +++ /dev/null @@ -1,80 +0,0 @@ -/* SPDX-License-Identifier: BSD-2-Clause */ -/* - * Copyright (C) 2019-2021, Raspberry Pi Ltd - * - * sdn.cpp - SDN (spatial denoise) control algorithm - */ - -#include - -#include "../denoise_status.h" -#include "../noise_status.h" - -#include "sdn.h" - -using namespace RPiController; -using namespace libcamera; - -LOG_DEFINE_CATEGORY(RPiSdn) - -/* - * Calculate settings for the spatial denoise block using the noise profile in - * the image metadata. - */ - -#define NAME "rpi.sdn" - -Sdn::Sdn(Controller *controller) - : DenoiseAlgorithm(controller), mode_(DenoiseMode::ColourOff) -{ -} - -char const *Sdn::name() const -{ - return NAME; -} - -int Sdn::read(const libcamera::YamlObject ¶ms) -{ - deviation_ = params["deviation"].get(3.2); - strength_ = params["strength"].get(0.75); - return 0; -} - -void Sdn::initialise() -{ -} - -void Sdn::prepare(Metadata *imageMetadata) -{ - struct NoiseStatus noiseStatus = {}; - noiseStatus.noiseSlope = 3.0; /* in case no metadata */ - if (imageMetadata->get("noise.status", noiseStatus) != 0) - LOG(RPiSdn, Warning) << "no noise profile found"; - LOG(RPiSdn, Debug) - << "Noise profile: constant " << noiseStatus.noiseConstant - << " slope " << noiseStatus.noiseSlope; - struct DenoiseStatus status; - status.noiseConstant = noiseStatus.noiseConstant * deviation_; - status.noiseSlope = noiseStatus.noiseSlope * deviation_; - status.strength = strength_; - status.mode = static_cast>(mode_); - imageMetadata->set("denoise.status", status); - LOG(RPiSdn, Debug) - << "programmed constant " << status.noiseConstant - << " slope " << status.noiseSlope - << " strength " << status.strength; -} - -void Sdn::setMode(DenoiseMode mode) -{ - /* We only distinguish between off and all other modes. */ - mode_ = mode; -} - -/* Register algorithm with the system. */ -static Algorithm *create(Controller *controller) -{ - return (Algorithm *)new Sdn(controller); -} -static RegisterAlgorithm reg(NAME, &create); diff --git a/src/ipa/raspberrypi/controller/rpi/sdn.h b/src/ipa/raspberrypi/controller/rpi/sdn.h deleted file mode 100644 index 9dd73c38..00000000 --- a/src/ipa/raspberrypi/controller/rpi/sdn.h +++ /dev/null @@ -1,32 +0,0 @@ -/* SPDX-License-Identifier: BSD-2-Clause */ -/* - * Copyright (C) 2019, Raspberry Pi Ltd - * - * sdn.h - SDN (spatial denoise) control algorithm - */ -#pragma once - -#include "../algorithm.h" -#include "../denoise_algorithm.h" - -namespace RPiController { - -/* Algorithm to calculate correct spatial denoise (SDN) settings. */ - -class Sdn : public DenoiseAlgorithm -{ -public: - Sdn(Controller *controller = NULL); - char const *name() const override; - int read(const libcamera::YamlObject ¶ms) override; - void initialise() override; - void prepare(Metadata *imageMetadata) override; - void setMode(DenoiseMode mode) override; - -private: - double deviation_; - double strength_; - DenoiseMode mode_; -}; - -} /* namespace RPiController */ diff --git a/src/ipa/raspberrypi/controller/rpi/sharpen.cpp b/src/ipa/raspberrypi/controller/rpi/sharpen.cpp deleted file mode 100644 index 4f6f020a..00000000 --- a/src/ipa/raspberrypi/controller/rpi/sharpen.cpp +++ /dev/null @@ -1,92 +0,0 @@ -/* SPDX-License-Identifier: BSD-2-Clause */ -/* - * Copyright (C) 2019, Raspberry Pi Ltd - * - * sharpen.cpp - sharpening control algorithm - */ - -#include - -#include - -#include "../sharpen_status.h" - -#include "sharpen.h" - -using namespace RPiController; -using namespace libcamera; - -LOG_DEFINE_CATEGORY(RPiSharpen) - -#define NAME "rpi.sharpen" - -Sharpen::Sharpen(Controller *controller) - : SharpenAlgorithm(controller), userStrength_(1.0) -{ -} - -char const *Sharpen::name() const -{ - return NAME; -} - -void Sharpen::switchMode(CameraMode const &cameraMode, - [[maybe_unused]] Metadata *metadata) -{ - /* can't be less than one, right? */ - modeFactor_ = std::max(1.0, cameraMode.noiseFactor); -} - -int Sharpen::read(const libcamera::YamlObject ¶ms) -{ - threshold_ = params["threshold"].get(1.0); - strength_ = params["strength"].get(1.0); - limit_ = params["limit"].get(1.0); - LOG(RPiSharpen, Debug) - << "Read threshold " << threshold_ - << " strength " << strength_ - << " limit " << limit_; - return 0; -} - -void Sharpen::setStrength(double strength) -{ - /* - * Note that this function is how an application sets the overall - * sharpening "strength". We call this the "user strength" field - * as there already is a strength_ field - being an internal gain - * parameter that gets passed to the ISP control code. Negative - * values are not allowed - coerce them to zero (no sharpening). - */ - userStrength_ = std::max(0.0, strength); -} - -void Sharpen::prepare(Metadata *imageMetadata) -{ - /* - * The userStrength_ affects the algorithm's internal gain directly, but - * we adjust the limit and threshold less aggressively. Using a sqrt - * function is an arbitrary but gentle way of accomplishing this. - */ - double userStrengthSqrt = sqrt(userStrength_); - struct SharpenStatus status; - /* - * Binned modes seem to need the sharpening toned down with this - * pipeline, thus we use the modeFactor_ here. Also avoid - * divide-by-zero with the userStrengthSqrt. - */ - status.threshold = threshold_ * modeFactor_ / - std::max(0.01, userStrengthSqrt); - status.strength = strength_ / modeFactor_ * userStrength_; - status.limit = limit_ / modeFactor_ * userStrengthSqrt; - /* Finally, report any application-supplied parameters that were used. */ - status.userStrength = userStrength_; - imageMetadata->set("sharpen.status", status); -} - -/* Register algorithm with the system. */ -static Algorithm *create(Controller *controller) -{ - return new Sharpen(controller); -} -static RegisterAlgorithm reg(NAME, &create); diff --git a/src/ipa/raspberrypi/controller/rpi/sharpen.h b/src/ipa/raspberrypi/controller/rpi/sharpen.h deleted file mode 100644 index 8bb7631e..00000000 --- a/src/ipa/raspberrypi/controller/rpi/sharpen.h +++ /dev/null @@ -1,34 +0,0 @@ -/* SPDX-License-Identifier: BSD-2-Clause */ -/* - * Copyright (C) 2019, Raspberry Pi Ltd - * - * sharpen.h - sharpening control algorithm - */ -#pragma once - -#include "../sharpen_algorithm.h" -#include "../sharpen_status.h" - -/* This is our implementation of the "sharpen algorithm". */ - -namespace RPiController { - -class Sharpen : public SharpenAlgorithm -{ -public: - Sharpen(Controller *controller); - char const *name() const override; - void switchMode(CameraMode const &cameraMode, Metadata *metadata) override; - int read(const libcamera::YamlObject ¶ms) override; - void setStrength(double strength) override; - void prepare(Metadata *imageMetadata) override; - -private: - double threshold_; - double strength_; - double limit_; - double modeFactor_; - double userStrength_; -}; - -} /* namespace RPiController */ diff --git a/src/ipa/raspberrypi/controller/sharpen_algorithm.h b/src/ipa/raspberrypi/controller/sharpen_algorithm.h deleted file mode 100644 index 3be21c32..00000000 --- a/src/ipa/raspberrypi/controller/sharpen_algorithm.h +++ /dev/null @@ -1,21 +0,0 @@ -/* SPDX-License-Identifier: BSD-2-Clause */ -/* - * Copyright (C) 2020, Raspberry Pi Ltd - * - * sharpen_algorithm.h - sharpness control algorithm interface - */ -#pragma once - -#include "algorithm.h" - -namespace RPiController { - -class SharpenAlgorithm : public Algorithm -{ -public: - SharpenAlgorithm(Controller *controller) : Algorithm(controller) {} - /* A sharpness control algorithm must provide the following: */ - virtual void setStrength(double strength) = 0; -}; - -} /* namespace RPiController */ diff --git a/src/ipa/raspberrypi/controller/sharpen_status.h b/src/ipa/raspberrypi/controller/sharpen_status.h deleted file mode 100644 index 106166db..00000000 --- a/src/ipa/raspberrypi/controller/sharpen_status.h +++ /dev/null @@ -1,20 +0,0 @@ -/* SPDX-License-Identifier: BSD-2-Clause */ -/* - * Copyright (C) 2019, Raspberry Pi Ltd - * - * sharpen_status.h - Sharpen control algorithm status - */ -#pragma once - -/* The "sharpen" algorithm stores the strength to use. */ - -struct SharpenStatus { - /* controls the smallest level of detail (or noise!) that sharpening will pick up */ - double threshold; - /* the rate at which the sharpening response ramps once above the threshold */ - double strength; - /* upper limit of the allowed sharpening response */ - double limit; - /* The sharpening strength requested by the user or application. */ - double userStrength; -}; diff --git a/src/ipa/raspberrypi/data/imx219.json b/src/ipa/raspberrypi/data/imx219.json deleted file mode 100644 index efe7210a..00000000 --- a/src/ipa/raspberrypi/data/imx219.json +++ /dev/null @@ -1,486 +0,0 @@ -{ - "version": 2.0, - "target": "bcm2835", - "algorithms": [ - { - "rpi.black_level": - { - "black_level": 4096 - } - }, - { - "rpi.dpc": { } - }, - { - "rpi.lux": - { - "reference_shutter_speed": 27685, - "reference_gain": 1.0, - "reference_aperture": 1.0, - "reference_lux": 998, - "reference_Y": 12744 - } - }, - { - "rpi.noise": - { - "reference_constant": 0, - "reference_slope": 3.67 - } - }, - { - "rpi.geq": - { - "offset": 204, - "slope": 0.01633 - } - }, - { - "rpi.sdn": { } - }, - { - "rpi.awb": - { - "priors": [ - { - "lux": 0, - "prior": - [ - 2000, 1.0, - 3000, 0.0, - 13000, 0.0 - ] - }, - { - "lux": 800, - "prior": - [ - 2000, 0.0, - 6000, 2.0, - 13000, 2.0 - ] - }, - { - "lux": 1500, - "prior": - [ - 2000, 0.0, - 4000, 1.0, - 6000, 6.0, - 6500, 7.0, - 7000, 1.0, - 13000, 1.0 - ] - } - ], - "modes": - { - "auto": - { - "lo": 2500, - "hi": 8000 - }, - "incandescent": - { - "lo": 2500, - "hi": 3000 - }, - "tungsten": - { - "lo": 3000, - "hi": 3500 - }, - "fluorescent": - { - "lo": 4000, - "hi": 4700 - }, - "indoor": - { - "lo": 3000, - "hi": 5000 - }, - "daylight": - { - "lo": 5500, - "hi": 6500 - }, - "cloudy": - { - "lo": 7000, - "hi": 8600 - } - }, - "bayes": 1, - "ct_curve": - [ - 2498.0, 0.9309, 0.3599, - 2911.0, 0.8682, 0.4283, - 2919.0, 0.8358, 0.4621, - 3627.0, 0.7646, 0.5327, - 4600.0, 0.6079, 0.6721, - 5716.0, 0.5712, 0.7017, - 8575.0, 0.4331, 0.8037 - ], - "sensitivity_r": 1.05, - "sensitivity_b": 1.05, - "transverse_pos": 0.04791, - "transverse_neg": 0.04881 - } - }, - { - "rpi.agc": - { - "metering_modes": - { - "centre-weighted": - { - "weights": [ 3, 3, 3, 2, 2, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0 ] - }, - "spot": - { - "weights": [ 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ] - }, - "matrix": - { - "weights": [ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ] - } - }, - "exposure_modes": - { - "normal": - { - "shutter": [ 100, 10000, 30000, 60000, 66666 ], - "gain": [ 1.0, 2.0, 4.0, 6.0, 8.0 ] - }, - "short": - { - "shutter": [ 100, 5000, 10000, 20000, 33333 ], - "gain": [ 1.0, 2.0, 4.0, 6.0, 8.0 ] - }, - "long": - { - "shutter": [ 100, 10000, 30000, 60000, 120000 ], - "gain": [ 1.0, 2.0, 4.0, 6.0, 12.0 ] - } - }, - "constraint_modes": - { - "normal": [ - { - "bound": "LOWER", - "q_lo": 0.98, - "q_hi": 1.0, - "y_target": - [ - 0, 0.5, - 1000, 0.5 - ] - } - ], - "highlight": [ - { - "bound": "LOWER", - "q_lo": 0.98, - "q_hi": 1.0, - "y_target": - [ - 0, 0.5, - 1000, 0.5 - ] - }, - { - "bound": "UPPER", - "q_lo": 0.98, - "q_hi": 1.0, - "y_target": - [ - 0, 0.8, - 1000, 0.8 - ] - } - ], - "shadows": [ - { - "bound": "LOWER", - "q_lo": 0.0, - "q_hi": 0.5, - "y_target": - [ - 0, 0.17, - 1000, 0.17 - ] - } - ] - }, - "y_target": - [ - 0, 0.16, - 1000, 0.165, - 10000, 0.17 - ] - } - }, - { - "rpi.alsc": - { - "omega": 1.3, - "n_iter": 100, - "luminance_strength": 0.7, - "calibrations_Cr": [ - { - "ct": 3000, - "table": - [ - 1.487, 1.481, 1.481, 1.445, 1.389, 1.327, 1.307, 1.307, 1.307, 1.309, 1.341, 1.405, 1.458, 1.494, 1.494, 1.497, - 1.491, 1.481, 1.448, 1.397, 1.331, 1.275, 1.243, 1.229, 1.229, 1.249, 1.287, 1.349, 1.409, 1.463, 1.494, 1.497, - 1.491, 1.469, 1.405, 1.331, 1.275, 1.217, 1.183, 1.172, 1.172, 1.191, 1.231, 1.287, 1.349, 1.424, 1.484, 1.499, - 1.487, 1.444, 1.363, 1.283, 1.217, 1.183, 1.148, 1.138, 1.138, 1.159, 1.191, 1.231, 1.302, 1.385, 1.461, 1.492, - 1.481, 1.423, 1.334, 1.253, 1.189, 1.148, 1.135, 1.119, 1.123, 1.137, 1.159, 1.203, 1.272, 1.358, 1.442, 1.488, - 1.479, 1.413, 1.321, 1.236, 1.176, 1.139, 1.118, 1.114, 1.116, 1.123, 1.149, 1.192, 1.258, 1.344, 1.432, 1.487, - 1.479, 1.413, 1.321, 1.236, 1.176, 1.139, 1.116, 1.114, 1.115, 1.123, 1.149, 1.192, 1.258, 1.344, 1.432, 1.487, - 1.479, 1.425, 1.336, 1.251, 1.189, 1.149, 1.136, 1.118, 1.121, 1.138, 1.158, 1.206, 1.275, 1.358, 1.443, 1.488, - 1.488, 1.448, 1.368, 1.285, 1.219, 1.189, 1.149, 1.139, 1.139, 1.158, 1.195, 1.235, 1.307, 1.387, 1.462, 1.493, - 1.496, 1.475, 1.411, 1.337, 1.284, 1.219, 1.189, 1.176, 1.176, 1.195, 1.235, 1.296, 1.356, 1.429, 1.487, 1.501, - 1.495, 1.489, 1.458, 1.407, 1.337, 1.287, 1.253, 1.239, 1.239, 1.259, 1.296, 1.356, 1.419, 1.472, 1.499, 1.499, - 1.494, 1.489, 1.489, 1.453, 1.398, 1.336, 1.317, 1.317, 1.317, 1.321, 1.351, 1.416, 1.467, 1.501, 1.501, 1.499 - ] - }, - { - "ct": 3850, - "table": - [ - 1.694, 1.688, 1.688, 1.649, 1.588, 1.518, 1.495, 1.495, 1.495, 1.497, 1.532, 1.602, 1.659, 1.698, 1.698, 1.703, - 1.698, 1.688, 1.653, 1.597, 1.525, 1.464, 1.429, 1.413, 1.413, 1.437, 1.476, 1.542, 1.606, 1.665, 1.698, 1.703, - 1.697, 1.673, 1.605, 1.525, 1.464, 1.401, 1.369, 1.354, 1.354, 1.377, 1.417, 1.476, 1.542, 1.623, 1.687, 1.705, - 1.692, 1.646, 1.561, 1.472, 1.401, 1.368, 1.337, 1.323, 1.324, 1.348, 1.377, 1.417, 1.492, 1.583, 1.661, 1.697, - 1.686, 1.625, 1.528, 1.439, 1.372, 1.337, 1.321, 1.311, 1.316, 1.324, 1.348, 1.389, 1.461, 1.553, 1.642, 1.694, - 1.684, 1.613, 1.514, 1.423, 1.359, 1.328, 1.311, 1.306, 1.306, 1.316, 1.339, 1.378, 1.446, 1.541, 1.633, 1.693, - 1.684, 1.613, 1.514, 1.423, 1.359, 1.328, 1.311, 1.305, 1.305, 1.316, 1.339, 1.378, 1.446, 1.541, 1.633, 1.693, - 1.685, 1.624, 1.529, 1.438, 1.372, 1.336, 1.324, 1.309, 1.314, 1.323, 1.348, 1.392, 1.462, 1.555, 1.646, 1.694, - 1.692, 1.648, 1.561, 1.473, 1.403, 1.372, 1.336, 1.324, 1.324, 1.348, 1.378, 1.423, 1.495, 1.585, 1.667, 1.701, - 1.701, 1.677, 1.608, 1.527, 1.471, 1.403, 1.375, 1.359, 1.359, 1.378, 1.423, 1.488, 1.549, 1.631, 1.694, 1.709, - 1.702, 1.694, 1.656, 1.601, 1.527, 1.473, 1.441, 1.424, 1.424, 1.443, 1.488, 1.549, 1.621, 1.678, 1.706, 1.707, - 1.699, 1.694, 1.694, 1.654, 1.593, 1.525, 1.508, 1.508, 1.508, 1.509, 1.546, 1.614, 1.674, 1.708, 1.708, 1.707 - ] - }, - { - "ct": 6000, - "table": - [ - 2.179, 2.176, 2.176, 2.125, 2.048, 1.975, 1.955, 1.954, 1.954, 1.956, 1.993, 2.071, 2.141, 2.184, 2.185, 2.188, - 2.189, 2.176, 2.128, 2.063, 1.973, 1.908, 1.872, 1.856, 1.856, 1.876, 1.922, 1.999, 2.081, 2.144, 2.184, 2.192, - 2.187, 2.152, 2.068, 1.973, 1.907, 1.831, 1.797, 1.786, 1.786, 1.804, 1.853, 1.922, 1.999, 2.089, 2.166, 2.191, - 2.173, 2.117, 2.013, 1.908, 1.831, 1.791, 1.755, 1.749, 1.749, 1.767, 1.804, 1.853, 1.939, 2.041, 2.135, 2.181, - 2.166, 2.089, 1.975, 1.869, 1.792, 1.755, 1.741, 1.731, 1.734, 1.749, 1.767, 1.818, 1.903, 2.005, 2.111, 2.173, - 2.165, 2.074, 1.956, 1.849, 1.777, 1.742, 1.729, 1.725, 1.729, 1.734, 1.758, 1.804, 1.884, 1.991, 2.099, 2.172, - 2.165, 2.074, 1.956, 1.849, 1.777, 1.742, 1.727, 1.724, 1.725, 1.734, 1.758, 1.804, 1.884, 1.991, 2.099, 2.172, - 2.166, 2.085, 1.975, 1.869, 1.791, 1.755, 1.741, 1.729, 1.733, 1.749, 1.769, 1.819, 1.904, 2.009, 2.114, 2.174, - 2.174, 2.118, 2.015, 1.913, 1.831, 1.791, 1.755, 1.749, 1.749, 1.769, 1.811, 1.855, 1.943, 2.047, 2.139, 2.183, - 2.187, 2.151, 2.072, 1.979, 1.911, 1.831, 1.801, 1.791, 1.791, 1.811, 1.855, 1.933, 2.006, 2.101, 2.173, 2.197, - 2.189, 2.178, 2.132, 2.069, 1.979, 1.913, 1.879, 1.867, 1.867, 1.891, 1.933, 2.006, 2.091, 2.156, 2.195, 2.197, - 2.181, 2.179, 2.178, 2.131, 2.057, 1.981, 1.965, 1.965, 1.965, 1.969, 1.999, 2.083, 2.153, 2.197, 2.197, 2.196 - ] - } - ], - "calibrations_Cb": [ - { - "ct": 3000, - "table": - [ - 1.967, 1.961, 1.955, 1.953, 1.954, 1.957, 1.961, 1.963, 1.963, 1.961, 1.959, 1.957, 1.954, 1.951, 1.951, 1.955, - 1.961, 1.959, 1.957, 1.956, 1.962, 1.967, 1.975, 1.979, 1.979, 1.975, 1.971, 1.967, 1.957, 1.952, 1.951, 1.951, - 1.959, 1.959, 1.959, 1.966, 1.976, 1.989, 1.999, 2.004, 2.003, 1.997, 1.991, 1.981, 1.967, 1.956, 1.951, 1.951, - 1.959, 1.962, 1.967, 1.978, 1.993, 2.009, 2.021, 2.028, 2.026, 2.021, 2.011, 1.995, 1.981, 1.964, 1.953, 1.951, - 1.961, 1.965, 1.977, 1.993, 2.009, 2.023, 2.041, 2.047, 2.047, 2.037, 2.024, 2.011, 1.995, 1.975, 1.958, 1.953, - 1.963, 1.968, 1.981, 2.001, 2.019, 2.039, 2.046, 2.052, 2.052, 2.051, 2.035, 2.021, 2.001, 1.978, 1.959, 1.955, - 1.961, 1.966, 1.981, 2.001, 2.019, 2.038, 2.043, 2.051, 2.052, 2.042, 2.034, 2.019, 2.001, 1.978, 1.959, 1.954, - 1.957, 1.961, 1.972, 1.989, 2.003, 2.021, 2.038, 2.039, 2.039, 2.034, 2.019, 2.004, 1.988, 1.971, 1.954, 1.949, - 1.952, 1.953, 1.959, 1.972, 1.989, 2.003, 2.016, 2.019, 2.019, 2.014, 2.003, 1.988, 1.971, 1.955, 1.948, 1.947, - 1.949, 1.948, 1.949, 1.957, 1.971, 1.978, 1.991, 1.994, 1.994, 1.989, 1.979, 1.967, 1.954, 1.946, 1.947, 1.947, - 1.949, 1.946, 1.944, 1.946, 1.949, 1.954, 1.962, 1.967, 1.967, 1.963, 1.956, 1.948, 1.943, 1.943, 1.946, 1.949, - 1.951, 1.946, 1.944, 1.942, 1.943, 1.943, 1.947, 1.948, 1.949, 1.947, 1.945, 1.941, 1.938, 1.939, 1.948, 1.952 - ] - }, - { - "ct": 3850, - "table": - [ - 1.726, 1.724, 1.722, 1.723, 1.731, 1.735, 1.743, 1.746, 1.746, 1.741, 1.735, 1.729, 1.725, 1.721, 1.721, 1.721, - 1.724, 1.723, 1.723, 1.727, 1.735, 1.744, 1.749, 1.756, 1.756, 1.749, 1.744, 1.735, 1.727, 1.719, 1.719, 1.719, - 1.723, 1.723, 1.724, 1.735, 1.746, 1.759, 1.767, 1.775, 1.775, 1.766, 1.758, 1.746, 1.735, 1.723, 1.718, 1.716, - 1.723, 1.725, 1.732, 1.746, 1.759, 1.775, 1.782, 1.792, 1.792, 1.782, 1.772, 1.759, 1.745, 1.729, 1.718, 1.716, - 1.725, 1.729, 1.738, 1.756, 1.775, 1.785, 1.796, 1.803, 1.804, 1.794, 1.783, 1.772, 1.757, 1.736, 1.722, 1.718, - 1.728, 1.731, 1.741, 1.759, 1.781, 1.795, 1.803, 1.806, 1.808, 1.805, 1.791, 1.779, 1.762, 1.739, 1.722, 1.721, - 1.727, 1.731, 1.741, 1.759, 1.781, 1.791, 1.799, 1.804, 1.806, 1.801, 1.791, 1.779, 1.762, 1.739, 1.722, 1.717, - 1.722, 1.724, 1.733, 1.751, 1.768, 1.781, 1.791, 1.796, 1.799, 1.791, 1.781, 1.766, 1.754, 1.731, 1.717, 1.714, - 1.718, 1.718, 1.724, 1.737, 1.752, 1.768, 1.776, 1.782, 1.784, 1.781, 1.766, 1.754, 1.737, 1.724, 1.713, 1.709, - 1.716, 1.715, 1.716, 1.725, 1.737, 1.749, 1.756, 1.763, 1.764, 1.762, 1.749, 1.737, 1.724, 1.717, 1.709, 1.708, - 1.715, 1.714, 1.712, 1.715, 1.722, 1.729, 1.736, 1.741, 1.742, 1.739, 1.731, 1.723, 1.717, 1.712, 1.711, 1.709, - 1.716, 1.714, 1.711, 1.712, 1.715, 1.719, 1.723, 1.728, 1.731, 1.729, 1.723, 1.718, 1.711, 1.711, 1.713, 1.713 - ] - }, - { - "ct": 6000, - "table": - [ - 1.374, 1.372, 1.373, 1.374, 1.375, 1.378, 1.378, 1.381, 1.382, 1.382, 1.378, 1.373, 1.372, 1.369, 1.365, 1.365, - 1.371, 1.371, 1.372, 1.374, 1.378, 1.381, 1.384, 1.386, 1.388, 1.387, 1.384, 1.377, 1.372, 1.368, 1.364, 1.362, - 1.369, 1.371, 1.372, 1.377, 1.383, 1.391, 1.394, 1.396, 1.397, 1.395, 1.391, 1.382, 1.374, 1.369, 1.362, 1.361, - 1.369, 1.371, 1.375, 1.383, 1.391, 1.399, 1.402, 1.404, 1.405, 1.403, 1.398, 1.391, 1.379, 1.371, 1.363, 1.361, - 1.371, 1.373, 1.378, 1.388, 1.399, 1.407, 1.411, 1.413, 1.413, 1.411, 1.405, 1.397, 1.385, 1.374, 1.366, 1.362, - 1.371, 1.374, 1.379, 1.389, 1.405, 1.411, 1.414, 1.414, 1.415, 1.415, 1.411, 1.401, 1.388, 1.376, 1.367, 1.363, - 1.371, 1.373, 1.379, 1.389, 1.405, 1.408, 1.413, 1.414, 1.414, 1.413, 1.409, 1.401, 1.388, 1.376, 1.367, 1.362, - 1.366, 1.369, 1.374, 1.384, 1.396, 1.404, 1.407, 1.408, 1.408, 1.408, 1.401, 1.395, 1.382, 1.371, 1.363, 1.359, - 1.364, 1.365, 1.368, 1.375, 1.386, 1.396, 1.399, 1.401, 1.399, 1.399, 1.395, 1.385, 1.374, 1.365, 1.359, 1.357, - 1.361, 1.363, 1.365, 1.368, 1.377, 1.384, 1.388, 1.391, 1.391, 1.388, 1.385, 1.375, 1.366, 1.361, 1.358, 1.356, - 1.361, 1.362, 1.362, 1.364, 1.367, 1.373, 1.376, 1.377, 1.377, 1.375, 1.373, 1.366, 1.362, 1.358, 1.358, 1.358, - 1.361, 1.362, 1.362, 1.362, 1.363, 1.367, 1.369, 1.368, 1.367, 1.367, 1.367, 1.364, 1.358, 1.357, 1.358, 1.359 - ] - } - ], - "luminance_lut": - [ - 2.716, 2.568, 2.299, 2.065, 1.845, 1.693, 1.605, 1.597, 1.596, 1.634, 1.738, 1.914, 2.145, 2.394, 2.719, 2.901, - 2.593, 2.357, 2.093, 1.876, 1.672, 1.528, 1.438, 1.393, 1.394, 1.459, 1.569, 1.731, 1.948, 2.169, 2.481, 2.756, - 2.439, 2.197, 1.922, 1.691, 1.521, 1.365, 1.266, 1.222, 1.224, 1.286, 1.395, 1.573, 1.747, 1.988, 2.299, 2.563, - 2.363, 2.081, 1.797, 1.563, 1.376, 1.244, 1.152, 1.099, 1.101, 1.158, 1.276, 1.421, 1.607, 1.851, 2.163, 2.455, - 2.342, 2.003, 1.715, 1.477, 1.282, 1.152, 1.074, 1.033, 1.035, 1.083, 1.163, 1.319, 1.516, 1.759, 2.064, 2.398, - 2.342, 1.985, 1.691, 1.446, 1.249, 1.111, 1.034, 1.004, 1.004, 1.028, 1.114, 1.274, 1.472, 1.716, 2.019, 2.389, - 2.342, 1.991, 1.691, 1.446, 1.249, 1.112, 1.034, 1.011, 1.005, 1.035, 1.114, 1.274, 1.472, 1.716, 2.019, 2.389, - 2.365, 2.052, 1.751, 1.499, 1.299, 1.171, 1.089, 1.039, 1.042, 1.084, 1.162, 1.312, 1.516, 1.761, 2.059, 2.393, - 2.434, 2.159, 1.856, 1.601, 1.403, 1.278, 1.166, 1.114, 1.114, 1.162, 1.266, 1.402, 1.608, 1.847, 2.146, 2.435, - 2.554, 2.306, 2.002, 1.748, 1.563, 1.396, 1.299, 1.247, 1.243, 1.279, 1.386, 1.551, 1.746, 1.977, 2.272, 2.518, - 2.756, 2.493, 2.195, 1.947, 1.739, 1.574, 1.481, 1.429, 1.421, 1.457, 1.559, 1.704, 1.929, 2.159, 2.442, 2.681, - 2.935, 2.739, 2.411, 2.151, 1.922, 1.749, 1.663, 1.628, 1.625, 1.635, 1.716, 1.872, 2.113, 2.368, 2.663, 2.824 - ], - "sigma": 0.00381, - "sigma_Cb": 0.00216 - } - }, - { - "rpi.contrast": - { - "ce_enable": 1, - "gamma_curve": - [ - 0, 0, - 1024, 5040, - 2048, 9338, - 3072, 12356, - 4096, 15312, - 5120, 18051, - 6144, 20790, - 7168, 23193, - 8192, 25744, - 9216, 27942, - 10240, 30035, - 11264, 32005, - 12288, 33975, - 13312, 35815, - 14336, 37600, - 15360, 39168, - 16384, 40642, - 18432, 43379, - 20480, 45749, - 22528, 47753, - 24576, 49621, - 26624, 51253, - 28672, 52698, - 30720, 53796, - 32768, 54876, - 36864, 57012, - 40960, 58656, - 45056, 59954, - 49152, 61183, - 53248, 62355, - 57344, 63419, - 61440, 64476, - 65535, 65535 - ] - } - }, - { - "rpi.ccm": - { - "ccms": [ - { - "ct": 2498, - "ccm": - [ - 1.58731, -0.18011, -0.40721, - -0.60639, 2.03422, -0.42782, - -0.19612, -1.69203, 2.88815 - ] - }, - { - "ct": 2811, - "ccm": - [ - 1.61593, -0.33164, -0.28429, - -0.55048, 1.97779, -0.42731, - -0.12042, -1.42847, 2.54889 - ] - }, - { - "ct": 2911, - "ccm": - [ - 1.62771, -0.41282, -0.21489, - -0.57991, 2.04176, -0.46186, - -0.07613, -1.13359, 2.20972 - ] - }, - { - "ct": 2919, - "ccm": - [ - 1.62661, -0.37736, -0.24925, - -0.52519, 1.95233, -0.42714, - -0.10842, -1.34929, 2.45771 - ] - }, - { - "ct": 3627, - "ccm": - [ - 1.70385, -0.57231, -0.13154, - -0.47763, 1.85998, -0.38235, - -0.07467, -0.82678, 1.90145 - ] - }, - { - "ct": 4600, - "ccm": - [ - 1.68486, -0.61085, -0.07402, - -0.41927, 2.04016, -0.62089, - -0.08633, -0.67672, 1.76305 - ] - }, - { - "ct": 5716, - "ccm": - [ - 1.80439, -0.73699, -0.06739, - -0.36073, 1.83327, -0.47255, - -0.08378, -0.56403, 1.64781 - ] - }, - { - "ct": 8575, - "ccm": - [ - 1.89357, -0.76427, -0.12931, - -0.27399, 2.15605, -0.88206, - -0.12035, -0.68256, 1.80292 - ] - } - ] - } - }, - { - "rpi.sharpen": { } - } - ] -} \ No newline at end of file diff --git a/src/ipa/raspberrypi/data/imx219_noir.json b/src/ipa/raspberrypi/data/imx219_noir.json deleted file mode 100644 index cfedb943..00000000 --- a/src/ipa/raspberrypi/data/imx219_noir.json +++ /dev/null @@ -1,402 +0,0 @@ -{ - "version": 2.0, - "target": "bcm2835", - "algorithms": [ - { - "rpi.black_level": - { - "black_level": 4096 - } - }, - { - "rpi.dpc": { } - }, - { - "rpi.lux": - { - "reference_shutter_speed": 27685, - "reference_gain": 1.0, - "reference_aperture": 1.0, - "reference_lux": 998, - "reference_Y": 12744 - } - }, - { - "rpi.noise": - { - "reference_constant": 0, - "reference_slope": 3.67 - } - }, - { - "rpi.geq": - { - "offset": 204, - "slope": 0.01633 - } - }, - { - "rpi.sdn": { } - }, - { - "rpi.awb": - { - "bayes": 0 - } - }, - { - "rpi.agc": - { - "metering_modes": - { - "centre-weighted": - { - "weights": [ 3, 3, 3, 2, 2, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0 ] - }, - "spot": - { - "weights": [ 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ] - }, - "matrix": - { - "weights": [ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ] - } - }, - "exposure_modes": - { - "normal": - { - "shutter": [ 100, 10000, 30000, 60000, 66666 ], - "gain": [ 1.0, 2.0, 4.0, 6.0, 8.0 ] - }, - "short": - { - "shutter": [ 100, 5000, 10000, 20000, 33333 ], - "gain": [ 1.0, 2.0, 4.0, 6.0, 8.0 ] - }, - "long": - { - "shutter": [ 100, 10000, 30000, 60000, 120000 ], - "gain": [ 1.0, 2.0, 4.0, 6.0, 12.0 ] - } - }, - "constraint_modes": - { - "normal": [ - { - "bound": "LOWER", - "q_lo": 0.98, - "q_hi": 1.0, - "y_target": - [ - 0, 0.5, - 1000, 0.5 - ] - } - ], - "highlight": [ - { - "bound": "LOWER", - "q_lo": 0.98, - "q_hi": 1.0, - "y_target": - [ - 0, 0.5, - 1000, 0.5 - ] - }, - { - "bound": "UPPER", - "q_lo": 0.98, - "q_hi": 1.0, - "y_target": - [ - 0, 0.8, - 1000, 0.8 - ] - } - ], - "shadows": [ - { - "bound": "LOWER", - "q_lo": 0.0, - "q_hi": 0.5, - "y_target": - [ - 0, 0.17, - 1000, 0.17 - ] - } - ] - }, - "y_target": - [ - 0, 0.16, - 1000, 0.165, - 10000, 0.17 - ] - } - }, - { - "rpi.alsc": - { - "omega": 1.3, - "n_iter": 100, - "luminance_strength": 0.7, - "calibrations_Cr": [ - { - "ct": 3000, - "table": - [ - 1.487, 1.481, 1.481, 1.445, 1.389, 1.327, 1.307, 1.307, 1.307, 1.309, 1.341, 1.405, 1.458, 1.494, 1.494, 1.497, - 1.491, 1.481, 1.448, 1.397, 1.331, 1.275, 1.243, 1.229, 1.229, 1.249, 1.287, 1.349, 1.409, 1.463, 1.494, 1.497, - 1.491, 1.469, 1.405, 1.331, 1.275, 1.217, 1.183, 1.172, 1.172, 1.191, 1.231, 1.287, 1.349, 1.424, 1.484, 1.499, - 1.487, 1.444, 1.363, 1.283, 1.217, 1.183, 1.148, 1.138, 1.138, 1.159, 1.191, 1.231, 1.302, 1.385, 1.461, 1.492, - 1.481, 1.423, 1.334, 1.253, 1.189, 1.148, 1.135, 1.119, 1.123, 1.137, 1.159, 1.203, 1.272, 1.358, 1.442, 1.488, - 1.479, 1.413, 1.321, 1.236, 1.176, 1.139, 1.118, 1.114, 1.116, 1.123, 1.149, 1.192, 1.258, 1.344, 1.432, 1.487, - 1.479, 1.413, 1.321, 1.236, 1.176, 1.139, 1.116, 1.114, 1.115, 1.123, 1.149, 1.192, 1.258, 1.344, 1.432, 1.487, - 1.479, 1.425, 1.336, 1.251, 1.189, 1.149, 1.136, 1.118, 1.121, 1.138, 1.158, 1.206, 1.275, 1.358, 1.443, 1.488, - 1.488, 1.448, 1.368, 1.285, 1.219, 1.189, 1.149, 1.139, 1.139, 1.158, 1.195, 1.235, 1.307, 1.387, 1.462, 1.493, - 1.496, 1.475, 1.411, 1.337, 1.284, 1.219, 1.189, 1.176, 1.176, 1.195, 1.235, 1.296, 1.356, 1.429, 1.487, 1.501, - 1.495, 1.489, 1.458, 1.407, 1.337, 1.287, 1.253, 1.239, 1.239, 1.259, 1.296, 1.356, 1.419, 1.472, 1.499, 1.499, - 1.494, 1.489, 1.489, 1.453, 1.398, 1.336, 1.317, 1.317, 1.317, 1.321, 1.351, 1.416, 1.467, 1.501, 1.501, 1.499 - ] - }, - { - "ct": 3850, - "table": - [ - 1.694, 1.688, 1.688, 1.649, 1.588, 1.518, 1.495, 1.495, 1.495, 1.497, 1.532, 1.602, 1.659, 1.698, 1.698, 1.703, - 1.698, 1.688, 1.653, 1.597, 1.525, 1.464, 1.429, 1.413, 1.413, 1.437, 1.476, 1.542, 1.606, 1.665, 1.698, 1.703, - 1.697, 1.673, 1.605, 1.525, 1.464, 1.401, 1.369, 1.354, 1.354, 1.377, 1.417, 1.476, 1.542, 1.623, 1.687, 1.705, - 1.692, 1.646, 1.561, 1.472, 1.401, 1.368, 1.337, 1.323, 1.324, 1.348, 1.377, 1.417, 1.492, 1.583, 1.661, 1.697, - 1.686, 1.625, 1.528, 1.439, 1.372, 1.337, 1.321, 1.311, 1.316, 1.324, 1.348, 1.389, 1.461, 1.553, 1.642, 1.694, - 1.684, 1.613, 1.514, 1.423, 1.359, 1.328, 1.311, 1.306, 1.306, 1.316, 1.339, 1.378, 1.446, 1.541, 1.633, 1.693, - 1.684, 1.613, 1.514, 1.423, 1.359, 1.328, 1.311, 1.305, 1.305, 1.316, 1.339, 1.378, 1.446, 1.541, 1.633, 1.693, - 1.685, 1.624, 1.529, 1.438, 1.372, 1.336, 1.324, 1.309, 1.314, 1.323, 1.348, 1.392, 1.462, 1.555, 1.646, 1.694, - 1.692, 1.648, 1.561, 1.473, 1.403, 1.372, 1.336, 1.324, 1.324, 1.348, 1.378, 1.423, 1.495, 1.585, 1.667, 1.701, - 1.701, 1.677, 1.608, 1.527, 1.471, 1.403, 1.375, 1.359, 1.359, 1.378, 1.423, 1.488, 1.549, 1.631, 1.694, 1.709, - 1.702, 1.694, 1.656, 1.601, 1.527, 1.473, 1.441, 1.424, 1.424, 1.443, 1.488, 1.549, 1.621, 1.678, 1.706, 1.707, - 1.699, 1.694, 1.694, 1.654, 1.593, 1.525, 1.508, 1.508, 1.508, 1.509, 1.546, 1.614, 1.674, 1.708, 1.708, 1.707 - ] - }, - { - "ct": 6000, - "table": - [ - 2.179, 2.176, 2.176, 2.125, 2.048, 1.975, 1.955, 1.954, 1.954, 1.956, 1.993, 2.071, 2.141, 2.184, 2.185, 2.188, - 2.189, 2.176, 2.128, 2.063, 1.973, 1.908, 1.872, 1.856, 1.856, 1.876, 1.922, 1.999, 2.081, 2.144, 2.184, 2.192, - 2.187, 2.152, 2.068, 1.973, 1.907, 1.831, 1.797, 1.786, 1.786, 1.804, 1.853, 1.922, 1.999, 2.089, 2.166, 2.191, - 2.173, 2.117, 2.013, 1.908, 1.831, 1.791, 1.755, 1.749, 1.749, 1.767, 1.804, 1.853, 1.939, 2.041, 2.135, 2.181, - 2.166, 2.089, 1.975, 1.869, 1.792, 1.755, 1.741, 1.731, 1.734, 1.749, 1.767, 1.818, 1.903, 2.005, 2.111, 2.173, - 2.165, 2.074, 1.956, 1.849, 1.777, 1.742, 1.729, 1.725, 1.729, 1.734, 1.758, 1.804, 1.884, 1.991, 2.099, 2.172, - 2.165, 2.074, 1.956, 1.849, 1.777, 1.742, 1.727, 1.724, 1.725, 1.734, 1.758, 1.804, 1.884, 1.991, 2.099, 2.172, - 2.166, 2.085, 1.975, 1.869, 1.791, 1.755, 1.741, 1.729, 1.733, 1.749, 1.769, 1.819, 1.904, 2.009, 2.114, 2.174, - 2.174, 2.118, 2.015, 1.913, 1.831, 1.791, 1.755, 1.749, 1.749, 1.769, 1.811, 1.855, 1.943, 2.047, 2.139, 2.183, - 2.187, 2.151, 2.072, 1.979, 1.911, 1.831, 1.801, 1.791, 1.791, 1.811, 1.855, 1.933, 2.006, 2.101, 2.173, 2.197, - 2.189, 2.178, 2.132, 2.069, 1.979, 1.913, 1.879, 1.867, 1.867, 1.891, 1.933, 2.006, 2.091, 2.156, 2.195, 2.197, - 2.181, 2.179, 2.178, 2.131, 2.057, 1.981, 1.965, 1.965, 1.965, 1.969, 1.999, 2.083, 2.153, 2.197, 2.197, 2.196 - ] - } - ], - "calibrations_Cb": [ - { - "ct": 3000, - "table": - [ - 1.967, 1.961, 1.955, 1.953, 1.954, 1.957, 1.961, 1.963, 1.963, 1.961, 1.959, 1.957, 1.954, 1.951, 1.951, 1.955, - 1.961, 1.959, 1.957, 1.956, 1.962, 1.967, 1.975, 1.979, 1.979, 1.975, 1.971, 1.967, 1.957, 1.952, 1.951, 1.951, - 1.959, 1.959, 1.959, 1.966, 1.976, 1.989, 1.999, 2.004, 2.003, 1.997, 1.991, 1.981, 1.967, 1.956, 1.951, 1.951, - 1.959, 1.962, 1.967, 1.978, 1.993, 2.009, 2.021, 2.028, 2.026, 2.021, 2.011, 1.995, 1.981, 1.964, 1.953, 1.951, - 1.961, 1.965, 1.977, 1.993, 2.009, 2.023, 2.041, 2.047, 2.047, 2.037, 2.024, 2.011, 1.995, 1.975, 1.958, 1.953, - 1.963, 1.968, 1.981, 2.001, 2.019, 2.039, 2.046, 2.052, 2.052, 2.051, 2.035, 2.021, 2.001, 1.978, 1.959, 1.955, - 1.961, 1.966, 1.981, 2.001, 2.019, 2.038, 2.043, 2.051, 2.052, 2.042, 2.034, 2.019, 2.001, 1.978, 1.959, 1.954, - 1.957, 1.961, 1.972, 1.989, 2.003, 2.021, 2.038, 2.039, 2.039, 2.034, 2.019, 2.004, 1.988, 1.971, 1.954, 1.949, - 1.952, 1.953, 1.959, 1.972, 1.989, 2.003, 2.016, 2.019, 2.019, 2.014, 2.003, 1.988, 1.971, 1.955, 1.948, 1.947, - 1.949, 1.948, 1.949, 1.957, 1.971, 1.978, 1.991, 1.994, 1.994, 1.989, 1.979, 1.967, 1.954, 1.946, 1.947, 1.947, - 1.949, 1.946, 1.944, 1.946, 1.949, 1.954, 1.962, 1.967, 1.967, 1.963, 1.956, 1.948, 1.943, 1.943, 1.946, 1.949, - 1.951, 1.946, 1.944, 1.942, 1.943, 1.943, 1.947, 1.948, 1.949, 1.947, 1.945, 1.941, 1.938, 1.939, 1.948, 1.952 - ] - }, - { - "ct": 3850, - "table": - [ - 1.726, 1.724, 1.722, 1.723, 1.731, 1.735, 1.743, 1.746, 1.746, 1.741, 1.735, 1.729, 1.725, 1.721, 1.721, 1.721, - 1.724, 1.723, 1.723, 1.727, 1.735, 1.744, 1.749, 1.756, 1.756, 1.749, 1.744, 1.735, 1.727, 1.719, 1.719, 1.719, - 1.723, 1.723, 1.724, 1.735, 1.746, 1.759, 1.767, 1.775, 1.775, 1.766, 1.758, 1.746, 1.735, 1.723, 1.718, 1.716, - 1.723, 1.725, 1.732, 1.746, 1.759, 1.775, 1.782, 1.792, 1.792, 1.782, 1.772, 1.759, 1.745, 1.729, 1.718, 1.716, - 1.725, 1.729, 1.738, 1.756, 1.775, 1.785, 1.796, 1.803, 1.804, 1.794, 1.783, 1.772, 1.757, 1.736, 1.722, 1.718, - 1.728, 1.731, 1.741, 1.759, 1.781, 1.795, 1.803, 1.806, 1.808, 1.805, 1.791, 1.779, 1.762, 1.739, 1.722, 1.721, - 1.727, 1.731, 1.741, 1.759, 1.781, 1.791, 1.799, 1.804, 1.806, 1.801, 1.791, 1.779, 1.762, 1.739, 1.722, 1.717, - 1.722, 1.724, 1.733, 1.751, 1.768, 1.781, 1.791, 1.796, 1.799, 1.791, 1.781, 1.766, 1.754, 1.731, 1.717, 1.714, - 1.718, 1.718, 1.724, 1.737, 1.752, 1.768, 1.776, 1.782, 1.784, 1.781, 1.766, 1.754, 1.737, 1.724, 1.713, 1.709, - 1.716, 1.715, 1.716, 1.725, 1.737, 1.749, 1.756, 1.763, 1.764, 1.762, 1.749, 1.737, 1.724, 1.717, 1.709, 1.708, - 1.715, 1.714, 1.712, 1.715, 1.722, 1.729, 1.736, 1.741, 1.742, 1.739, 1.731, 1.723, 1.717, 1.712, 1.711, 1.709, - 1.716, 1.714, 1.711, 1.712, 1.715, 1.719, 1.723, 1.728, 1.731, 1.729, 1.723, 1.718, 1.711, 1.711, 1.713, 1.713 - ] - }, - { - "ct": 6000, - "table": - [ - 1.374, 1.372, 1.373, 1.374, 1.375, 1.378, 1.378, 1.381, 1.382, 1.382, 1.378, 1.373, 1.372, 1.369, 1.365, 1.365, - 1.371, 1.371, 1.372, 1.374, 1.378, 1.381, 1.384, 1.386, 1.388, 1.387, 1.384, 1.377, 1.372, 1.368, 1.364, 1.362, - 1.369, 1.371, 1.372, 1.377, 1.383, 1.391, 1.394, 1.396, 1.397, 1.395, 1.391, 1.382, 1.374, 1.369, 1.362, 1.361, - 1.369, 1.371, 1.375, 1.383, 1.391, 1.399, 1.402, 1.404, 1.405, 1.403, 1.398, 1.391, 1.379, 1.371, 1.363, 1.361, - 1.371, 1.373, 1.378, 1.388, 1.399, 1.407, 1.411, 1.413, 1.413, 1.411, 1.405, 1.397, 1.385, 1.374, 1.366, 1.362, - 1.371, 1.374, 1.379, 1.389, 1.405, 1.411, 1.414, 1.414, 1.415, 1.415, 1.411, 1.401, 1.388, 1.376, 1.367, 1.363, - 1.371, 1.373, 1.379, 1.389, 1.405, 1.408, 1.413, 1.414, 1.414, 1.413, 1.409, 1.401, 1.388, 1.376, 1.367, 1.362, - 1.366, 1.369, 1.374, 1.384, 1.396, 1.404, 1.407, 1.408, 1.408, 1.408, 1.401, 1.395, 1.382, 1.371, 1.363, 1.359, - 1.364, 1.365, 1.368, 1.375, 1.386, 1.396, 1.399, 1.401, 1.399, 1.399, 1.395, 1.385, 1.374, 1.365, 1.359, 1.357, - 1.361, 1.363, 1.365, 1.368, 1.377, 1.384, 1.388, 1.391, 1.391, 1.388, 1.385, 1.375, 1.366, 1.361, 1.358, 1.356, - 1.361, 1.362, 1.362, 1.364, 1.367, 1.373, 1.376, 1.377, 1.377, 1.375, 1.373, 1.366, 1.362, 1.358, 1.358, 1.358, - 1.361, 1.362, 1.362, 1.362, 1.363, 1.367, 1.369, 1.368, 1.367, 1.367, 1.367, 1.364, 1.358, 1.357, 1.358, 1.359 - ] - } - ], - "luminance_lut": - [ - 2.716, 2.568, 2.299, 2.065, 1.845, 1.693, 1.605, 1.597, 1.596, 1.634, 1.738, 1.914, 2.145, 2.394, 2.719, 2.901, - 2.593, 2.357, 2.093, 1.876, 1.672, 1.528, 1.438, 1.393, 1.394, 1.459, 1.569, 1.731, 1.948, 2.169, 2.481, 2.756, - 2.439, 2.197, 1.922, 1.691, 1.521, 1.365, 1.266, 1.222, 1.224, 1.286, 1.395, 1.573, 1.747, 1.988, 2.299, 2.563, - 2.363, 2.081, 1.797, 1.563, 1.376, 1.244, 1.152, 1.099, 1.101, 1.158, 1.276, 1.421, 1.607, 1.851, 2.163, 2.455, - 2.342, 2.003, 1.715, 1.477, 1.282, 1.152, 1.074, 1.033, 1.035, 1.083, 1.163, 1.319, 1.516, 1.759, 2.064, 2.398, - 2.342, 1.985, 1.691, 1.446, 1.249, 1.111, 1.034, 1.004, 1.004, 1.028, 1.114, 1.274, 1.472, 1.716, 2.019, 2.389, - 2.342, 1.991, 1.691, 1.446, 1.249, 1.112, 1.034, 1.011, 1.005, 1.035, 1.114, 1.274, 1.472, 1.716, 2.019, 2.389, - 2.365, 2.052, 1.751, 1.499, 1.299, 1.171, 1.089, 1.039, 1.042, 1.084, 1.162, 1.312, 1.516, 1.761, 2.059, 2.393, - 2.434, 2.159, 1.856, 1.601, 1.403, 1.278, 1.166, 1.114, 1.114, 1.162, 1.266, 1.402, 1.608, 1.847, 2.146, 2.435, - 2.554, 2.306, 2.002, 1.748, 1.563, 1.396, 1.299, 1.247, 1.243, 1.279, 1.386, 1.551, 1.746, 1.977, 2.272, 2.518, - 2.756, 2.493, 2.195, 1.947, 1.739, 1.574, 1.481, 1.429, 1.421, 1.457, 1.559, 1.704, 1.929, 2.159, 2.442, 2.681, - 2.935, 2.739, 2.411, 2.151, 1.922, 1.749, 1.663, 1.628, 1.625, 1.635, 1.716, 1.872, 2.113, 2.368, 2.663, 2.824 - ], - "sigma": 0.00381, - "sigma_Cb": 0.00216 - } - }, - { - "rpi.contrast": - { - "ce_enable": 1, - "gamma_curve": - [ - 0, 0, - 1024, 5040, - 2048, 9338, - 3072, 12356, - 4096, 15312, - 5120, 18051, - 6144, 20790, - 7168, 23193, - 8192, 25744, - 9216, 27942, - 10240, 30035, - 11264, 32005, - 12288, 33975, - 13312, 35815, - 14336, 37600, - 15360, 39168, - 16384, 40642, - 18432, 43379, - 20480, 45749, - 22528, 47753, - 24576, 49621, - 26624, 51253, - 28672, 52698, - 30720, 53796, - 32768, 54876, - 36864, 57012, - 40960, 58656, - 45056, 59954, - 49152, 61183, - 53248, 62355, - 57344, 63419, - 61440, 64476, - 65535, 65535 - ] - } - }, - { - "rpi.ccm": - { - "ccms": [ - { - "ct": 2498, - "ccm": - [ - 1.58731, -0.18011, -0.40721, - -0.60639, 2.03422, -0.42782, - -0.19612, -1.69203, 2.88815 - ] - }, - { - "ct": 2811, - "ccm": - [ - 1.61593, -0.33164, -0.28429, - -0.55048, 1.97779, -0.42731, - -0.12042, -1.42847, 2.54889 - ] - }, - { - "ct": 2911, - "ccm": - [ - 1.62771, -0.41282, -0.21489, - -0.57991, 2.04176, -0.46186, - -0.07613, -1.13359, 2.20972 - ] - }, - { - "ct": 2919, - "ccm": - [ - 1.62661, -0.37736, -0.24925, - -0.52519, 1.95233, -0.42714, - -0.10842, -1.34929, 2.45771 - ] - }, - { - "ct": 3627, - "ccm": - [ - 1.70385, -0.57231, -0.13154, - -0.47763, 1.85998, -0.38235, - -0.07467, -0.82678, 1.90145 - ] - }, - { - "ct": 4600, - "ccm": - [ - 1.68486, -0.61085, -0.07402, - -0.41927, 2.04016, -0.62089, - -0.08633, -0.67672, 1.76305 - ] - }, - { - "ct": 5716, - "ccm": - [ - 1.80439, -0.73699, -0.06739, - -0.36073, 1.83327, -0.47255, - -0.08378, -0.56403, 1.64781 - ] - }, - { - "ct": 8575, - "ccm": - [ - 1.89357, -0.76427, -0.12931, - -0.27399, 2.15605, -0.88206, - -0.12035, -0.68256, 1.80292 - ] - } - ] - } - }, - { - "rpi.sharpen": { } - } - ] -} \ No newline at end of file diff --git a/src/ipa/raspberrypi/data/imx290.json b/src/ipa/raspberrypi/data/imx290.json deleted file mode 100644 index ace68d0e..00000000 --- a/src/ipa/raspberrypi/data/imx290.json +++ /dev/null @@ -1,200 +0,0 @@ -{ - "version": 2.0, - "target": "bcm2835", - "algorithms": [ - { - "rpi.black_level": - { - "black_level": 3840 - } - }, - { - "rpi.dpc": { } - }, - { - "rpi.lux": - { - "reference_shutter_speed": 6813, - "reference_gain": 1.0, - "reference_aperture": 1.0, - "reference_lux": 890, - "reference_Y": 12900 - } - }, - { - "rpi.noise": - { - "reference_constant": 0, - "reference_slope": 2.67 - } - }, - { - "rpi.geq": - { - "offset": 187, - "slope": 0.00842 - } - }, - { - "rpi.sdn": { } - }, - { - "rpi.awb": - { - "bayes": 0 - } - }, - { - "rpi.agc": - { - "speed": 0.2, - "metering_modes": - { - "matrix": - { - "weights": [ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ] - }, - "centre-weighted": - { - "weights": [ 3, 3, 3, 2, 2, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0 ] - }, - "spot": - { - "weights": [ 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ] - } - }, - "exposure_modes": - { - "normal": - { - "shutter": [ 10, 30000, 60000 ], - "gain": [ 1.0, 2.0, 8.0 ] - }, - "sport": - { - "shutter": [ 10, 5000, 10000, 20000, 120000 ], - "gain": [ 1.0, 2.0, 4.0, 6.0, 8.0 ] - } - }, - "constraint_modes": - { - "normal": [ ], - "highlight": [ - { - "bound": "LOWER", - "q_lo": 0.98, - "q_hi": 1.0, - "y_target": - [ - 0, 0.5, - 1000, 0.5 - ] - }, - { - "bound": "UPPER", - "q_lo": 0.98, - "q_hi": 1.0, - "y_target": - [ - 0, 0.8, - 1000, 0.8 - ] - } - ] - }, - "y_target": - [ - 0, 0.16, - 1000, 0.16, - 10000, 0.16 - ] - } - }, - { - "rpi.alsc": - { - "omega": 1.3, - "n_iter": 100, - "luminance_strength": 0.7, - "luminance_lut": - [ - 2.844, 2.349, 2.018, 1.775, 1.599, 1.466, 1.371, 1.321, 1.306, 1.316, 1.357, 1.439, 1.552, 1.705, 1.915, 2.221, - 2.576, 2.151, 1.851, 1.639, 1.478, 1.358, 1.272, 1.231, 1.218, 1.226, 1.262, 1.335, 1.438, 1.571, 1.766, 2.067, - 2.381, 2.005, 1.739, 1.545, 1.389, 1.278, 1.204, 1.166, 1.153, 1.161, 1.194, 1.263, 1.356, 1.489, 1.671, 1.943, - 2.242, 1.899, 1.658, 1.481, 1.329, 1.225, 1.156, 1.113, 1.096, 1.107, 1.143, 1.201, 1.289, 1.423, 1.607, 1.861, - 2.152, 1.831, 1.602, 1.436, 1.291, 1.193, 1.121, 1.069, 1.047, 1.062, 1.107, 1.166, 1.249, 1.384, 1.562, 1.801, - 2.104, 1.795, 1.572, 1.407, 1.269, 1.174, 1.099, 1.041, 1.008, 1.029, 1.083, 1.146, 1.232, 1.364, 1.547, 1.766, - 2.104, 1.796, 1.572, 1.403, 1.264, 1.171, 1.097, 1.036, 1.001, 1.025, 1.077, 1.142, 1.231, 1.363, 1.549, 1.766, - 2.148, 1.827, 1.594, 1.413, 1.276, 1.184, 1.114, 1.062, 1.033, 1.049, 1.092, 1.153, 1.242, 1.383, 1.577, 1.795, - 2.211, 1.881, 1.636, 1.455, 1.309, 1.214, 1.149, 1.104, 1.081, 1.089, 1.125, 1.184, 1.273, 1.423, 1.622, 1.846, - 2.319, 1.958, 1.698, 1.516, 1.362, 1.262, 1.203, 1.156, 1.137, 1.142, 1.171, 1.229, 1.331, 1.484, 1.682, 1.933, - 2.459, 2.072, 1.789, 1.594, 1.441, 1.331, 1.261, 1.219, 1.199, 1.205, 1.232, 1.301, 1.414, 1.571, 1.773, 2.052, - 2.645, 2.206, 1.928, 1.728, 1.559, 1.451, 1.352, 1.301, 1.282, 1.289, 1.319, 1.395, 1.519, 1.685, 1.904, 2.227 - ], - "sigma": 0.005, - "sigma_Cb": 0.005 - } - }, - { - "rpi.contrast": - { - "ce_enable": 1, - "gamma_curve": - [ - 0, 0, - 1024, 5040, - 2048, 9338, - 3072, 12356, - 4096, 15312, - 5120, 18051, - 6144, 20790, - 7168, 23193, - 8192, 25744, - 9216, 27942, - 10240, 30035, - 11264, 32005, - 12288, 33975, - 13312, 35815, - 14336, 37600, - 15360, 39168, - 16384, 40642, - 18432, 43379, - 20480, 45749, - 22528, 47753, - 24576, 49621, - 26624, 51253, - 28672, 52698, - 30720, 53796, - 32768, 54876, - 36864, 57012, - 40960, 58656, - 45056, 59954, - 49152, 61183, - 53248, 62355, - 57344, 63419, - 61440, 64476, - 65535, 65535 - ] - } - }, - { - "rpi.sharpen": { } - }, - { - "rpi.ccm": - { - "ccms": [ - { - "ct": 3900, - "ccm": - [ - 1.54659, -0.17707, -0.36953, - -0.51471, 1.72733, -0.21262, - 0.06667, -0.92279, 1.85612 - ] - } - ] - } - } - ] -} \ No newline at end of file diff --git a/src/ipa/raspberrypi/data/imx296.json b/src/ipa/raspberrypi/data/imx296.json deleted file mode 100644 index ae8722c4..00000000 --- a/src/ipa/raspberrypi/data/imx296.json +++ /dev/null @@ -1,537 +0,0 @@ -{ - "version": 2.0, - "target": "bcm2835", - "algorithms": [ - { - "rpi.black_level": - { - "black_level": 3840 - } - }, - { - "rpi.dpc": { } - }, - { - "rpi.lux": - { - "reference_shutter_speed": 7598, - "reference_gain": 1.0, - "reference_aperture": 1.0, - "reference_lux": 800, - "reference_Y": 14028 - } - }, - { - "rpi.noise": - { - "reference_constant": 0, - "reference_slope": 2.671 - } - }, - { - "rpi.geq": - { - "offset": 215, - "slope": 0.01058 - } - }, - { - "rpi.sdn": { } - }, - { - "rpi.awb": - { - "priors": [ - { - "lux": 0, - "prior": - [ - 2000, 1.0, - 3000, 0.0, - 13000, 0.0 - ] - }, - { - "lux": 800, - "prior": - [ - 2000, 0.0, - 6000, 2.0, - 13000, 2.0 - ] - }, - { - "lux": 1500, - "prior": - [ - 2000, 0.0, - 4000, 1.0, - 6000, 6.0, - 6500, 7.0, - 7000, 1.0, - 13000, 1.0 - ] - } - ], - "modes": - { - "auto": - { - "lo": 2500, - "hi": 7600 - }, - "incandescent": - { - "lo": 2500, - "hi": 3000 - }, - "tungsten": - { - "lo": 3000, - "hi": 3500 - }, - "fluorescent": - { - "lo": 4000, - "hi": 4700 - }, - "indoor": - { - "lo": 3000, - "hi": 5000 - }, - "daylight": - { - "lo": 5500, - "hi": 6500 - }, - "cloudy": - { - "lo": 7000, - "hi": 7600 - } - }, - "bayes": 1, - "ct_curve": - [ - 2500.0, 0.5386, 0.2458, - 2800.0, 0.4883, 0.3303, - 2900.0, 0.4855, 0.3349, - 3620.0, 0.4203, 0.4367, - 4560.0, 0.3455, 0.5444, - 5600.0, 0.2948, 0.6124, - 7400.0, 0.2336, 0.6894 - ], - "sensitivity_r": 1.05, - "sensitivity_b": 1.05, - "transverse_pos": 0.03093, - "transverse_neg": 0.02374 - } - }, - { - "rpi.agc": - { - "metering_modes": - { - "centre-weighted": - { - "weights": [ 3, 3, 3, 2, 2, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0 ] - }, - "spot": - { - "weights": [ 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ] - }, - "matrix": - { - "weights": [ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ] - } - }, - "exposure_modes": - { - "normal": - { - "shutter": [ 100, 30000, 45000, 60000, 120000 ], - "gain": [ 1.0, 1.0, 2.0, 4.0, 12.0 ] - }, - "short": - { - "shutter": [ 100, 5000, 10000, 20000, 30000 ], - "gain": [ 1.0, 2.0, 4.0, 8.0, 16.0 ] - } - }, - "constraint_modes": - { - "normal": [ - { - "bound": "LOWER", - "q_lo": 0.98, - "q_hi": 1.0, - "y_target": - [ - 0, 0.5, - 1000, 0.5 - ] - } - ], - "highlight": [ - { - "bound": "LOWER", - "q_lo": 0.98, - "q_hi": 1.0, - "y_target": - [ - 0, 0.5, - 1000, 0.5 - ] - }, - { - "bound": "UPPER", - "q_lo": 0.98, - "q_hi": 1.0, - "y_target": - [ - 0, 0.8, - 1000, 0.8 - ] - } - ] - }, - "y_target": - [ - 0, 0.16, - 1000, 0.165, - 10000, 0.17 - ] - } - }, - { - "rpi.alsc": - { - "omega": 1.3, - "n_iter": 100, - "luminance_strength": 0.5, - "calibrations_Cr": [ - { - "ct": 4000, - "table": - [ - 2.726, 2.736, 2.737, 2.739, 2.741, 2.741, 2.742, 2.742, 2.743, 2.743, 2.742, 2.742, 2.742, 2.742, 2.741, 2.739, - 2.728, 2.736, 2.739, 2.741, 2.742, 2.743, 2.744, 2.745, 2.746, 2.746, 2.745, 2.743, 2.742, 2.742, 2.742, 2.741, - 2.729, 2.737, 2.741, 2.744, 2.746, 2.747, 2.748, 2.749, 2.751, 2.751, 2.749, 2.746, 2.744, 2.743, 2.743, 2.743, - 2.729, 2.738, 2.743, 2.746, 2.749, 2.749, 2.751, 2.752, 2.753, 2.753, 2.752, 2.751, 2.746, 2.744, 2.744, 2.746, - 2.728, 2.737, 2.742, 2.746, 2.749, 2.751, 2.754, 2.755, 2.754, 2.755, 2.754, 2.751, 2.748, 2.746, 2.747, 2.748, - 2.724, 2.738, 2.742, 2.746, 2.749, 2.752, 2.755, 2.755, 2.755, 2.755, 2.754, 2.752, 2.749, 2.749, 2.748, 2.748, - 2.726, 2.738, 2.741, 2.745, 2.749, 2.753, 2.754, 2.755, 2.755, 2.755, 2.754, 2.753, 2.749, 2.748, 2.748, 2.748, - 2.726, 2.738, 2.741, 2.745, 2.746, 2.752, 2.753, 2.753, 2.753, 2.753, 2.754, 2.751, 2.748, 2.748, 2.746, 2.745, - 2.726, 2.736, 2.738, 2.742, 2.745, 2.749, 2.752, 2.753, 2.752, 2.752, 2.751, 2.749, 2.747, 2.745, 2.744, 2.742, - 2.724, 2.733, 2.736, 2.739, 2.742, 2.745, 2.748, 2.749, 2.749, 2.748, 2.748, 2.747, 2.744, 2.743, 2.742, 2.741, - 2.722, 2.726, 2.733, 2.735, 2.737, 2.741, 2.743, 2.744, 2.744, 2.744, 2.744, 2.742, 2.741, 2.741, 2.739, 2.737, - 2.719, 2.722, 2.727, 2.729, 2.731, 2.732, 2.734, 2.734, 2.735, 2.735, 2.735, 2.734, 2.733, 2.732, 2.732, 2.732 - ] - }, - { - "ct": 6000, - "table": - [ - 3.507, 3.522, 3.525, 3.527, 3.531, 3.533, 3.534, 3.535, 3.535, 3.536, 3.536, 3.537, 3.537, 3.538, 3.537, 3.536, - 3.511, 3.524, 3.528, 3.532, 3.533, 3.535, 3.537, 3.538, 3.538, 3.541, 3.539, 3.539, 3.539, 3.539, 3.538, 3.538, - 3.513, 3.528, 3.532, 3.535, 3.538, 3.542, 3.543, 3.546, 3.548, 3.551, 3.547, 3.543, 3.541, 3.541, 3.541, 3.541, - 3.513, 3.528, 3.533, 3.539, 3.544, 3.546, 3.548, 3.552, 3.553, 3.553, 3.552, 3.548, 3.543, 3.542, 3.542, 3.545, - 3.513, 3.528, 3.534, 3.541, 3.547, 3.549, 3.552, 3.553, 3.554, 3.554, 3.553, 3.549, 3.546, 3.544, 3.547, 3.549, - 3.508, 3.528, 3.533, 3.541, 3.548, 3.551, 3.553, 3.554, 3.555, 3.555, 3.555, 3.551, 3.548, 3.547, 3.549, 3.551, - 3.511, 3.529, 3.534, 3.541, 3.548, 3.551, 3.553, 3.555, 3.555, 3.555, 3.556, 3.554, 3.549, 3.548, 3.548, 3.548, - 3.511, 3.528, 3.533, 3.539, 3.546, 3.549, 3.553, 3.554, 3.554, 3.554, 3.554, 3.553, 3.549, 3.547, 3.547, 3.547, - 3.511, 3.527, 3.533, 3.536, 3.541, 3.547, 3.551, 3.553, 3.553, 3.552, 3.551, 3.551, 3.548, 3.544, 3.542, 3.543, - 3.507, 3.523, 3.528, 3.533, 3.538, 3.541, 3.546, 3.548, 3.549, 3.548, 3.548, 3.546, 3.542, 3.541, 3.541, 3.541, - 3.505, 3.514, 3.523, 3.527, 3.532, 3.537, 3.538, 3.544, 3.544, 3.544, 3.542, 3.541, 3.537, 3.537, 3.536, 3.535, - 3.503, 3.508, 3.515, 3.519, 3.521, 3.523, 3.524, 3.525, 3.526, 3.526, 3.527, 3.526, 3.524, 3.526, 3.527, 3.527 - ] - } - ], - "calibrations_Cb": [ - { - "ct": 4000, - "table": - [ - 2.032, 2.037, 2.039, 2.041, 2.041, 2.042, 2.043, 2.044, 2.045, 2.045, 2.044, 2.043, 2.042, 2.041, 2.041, 2.034, - 2.032, 2.036, 2.039, 2.041, 2.042, 2.042, 2.043, 2.044, 2.045, 2.046, 2.045, 2.044, 2.042, 2.041, 2.039, 2.035, - 2.032, 2.036, 2.038, 2.041, 2.043, 2.044, 2.044, 2.045, 2.046, 2.047, 2.047, 2.045, 2.043, 2.042, 2.041, 2.037, - 2.032, 2.035, 2.039, 2.042, 2.043, 2.044, 2.045, 2.046, 2.048, 2.048, 2.047, 2.046, 2.045, 2.044, 2.042, 2.039, - 2.031, 2.034, 2.037, 2.039, 2.043, 2.045, 2.045, 2.046, 2.047, 2.047, 2.047, 2.046, 2.045, 2.044, 2.043, 2.039, - 2.029, 2.033, 2.036, 2.039, 2.042, 2.043, 2.045, 2.046, 2.046, 2.046, 2.046, 2.046, 2.046, 2.045, 2.044, 2.041, - 2.028, 2.032, 2.035, 2.039, 2.041, 2.043, 2.044, 2.045, 2.045, 2.046, 2.046, 2.046, 2.046, 2.045, 2.044, 2.039, - 2.027, 2.032, 2.035, 2.038, 2.039, 2.041, 2.044, 2.044, 2.044, 2.045, 2.046, 2.046, 2.046, 2.045, 2.044, 2.039, - 2.027, 2.031, 2.034, 2.035, 2.037, 2.039, 2.042, 2.043, 2.044, 2.045, 2.045, 2.046, 2.045, 2.044, 2.043, 2.038, - 2.025, 2.028, 2.032, 2.034, 2.036, 2.037, 2.041, 2.042, 2.043, 2.044, 2.044, 2.044, 2.044, 2.043, 2.041, 2.036, - 2.024, 2.026, 2.029, 2.032, 2.034, 2.036, 2.038, 2.041, 2.041, 2.042, 2.043, 2.042, 2.041, 2.041, 2.037, 2.036, - 2.022, 2.024, 2.027, 2.029, 2.032, 2.034, 2.036, 2.039, 2.039, 2.039, 2.041, 2.039, 2.039, 2.038, 2.036, 2.034 - ] - }, - { - "ct": 6000, - "table": - [ - 1.585, 1.587, 1.589, 1.589, 1.589, 1.591, 1.591, 1.591, 1.591, 1.591, 1.589, 1.589, 1.588, 1.588, 1.587, 1.581, - 1.585, 1.587, 1.588, 1.589, 1.591, 1.591, 1.591, 1.591, 1.591, 1.591, 1.591, 1.589, 1.588, 1.588, 1.587, 1.582, - 1.585, 1.586, 1.588, 1.589, 1.591, 1.591, 1.591, 1.591, 1.592, 1.592, 1.591, 1.591, 1.589, 1.588, 1.587, 1.584, - 1.585, 1.586, 1.588, 1.589, 1.591, 1.592, 1.592, 1.592, 1.593, 1.593, 1.592, 1.591, 1.589, 1.589, 1.588, 1.586, - 1.584, 1.586, 1.587, 1.589, 1.591, 1.591, 1.592, 1.592, 1.592, 1.592, 1.591, 1.591, 1.591, 1.589, 1.589, 1.586, - 1.583, 1.585, 1.587, 1.588, 1.589, 1.591, 1.591, 1.592, 1.592, 1.591, 1.591, 1.591, 1.591, 1.591, 1.589, 1.586, - 1.583, 1.584, 1.586, 1.588, 1.589, 1.589, 1.591, 1.591, 1.591, 1.591, 1.591, 1.591, 1.591, 1.591, 1.589, 1.585, - 1.581, 1.584, 1.586, 1.587, 1.588, 1.588, 1.589, 1.591, 1.591, 1.591, 1.591, 1.591, 1.591, 1.589, 1.589, 1.585, - 1.581, 1.583, 1.584, 1.586, 1.587, 1.588, 1.589, 1.589, 1.591, 1.591, 1.591, 1.591, 1.591, 1.589, 1.589, 1.585, - 1.579, 1.581, 1.583, 1.584, 1.586, 1.586, 1.588, 1.589, 1.589, 1.589, 1.589, 1.589, 1.589, 1.589, 1.587, 1.584, - 1.578, 1.579, 1.581, 1.583, 1.584, 1.585, 1.586, 1.587, 1.588, 1.588, 1.588, 1.588, 1.588, 1.587, 1.585, 1.583, - 1.577, 1.578, 1.579, 1.582, 1.583, 1.584, 1.585, 1.586, 1.586, 1.587, 1.587, 1.587, 1.586, 1.586, 1.584, 1.583 - ] - } - ], - "luminance_lut": - [ - 1.112, 1.098, 1.078, 1.062, 1.049, 1.039, 1.031, 1.027, 1.026, 1.027, 1.034, 1.043, 1.054, 1.069, 1.087, 1.096, - 1.106, 1.091, 1.073, 1.056, 1.042, 1.032, 1.025, 1.021, 1.021, 1.022, 1.027, 1.036, 1.047, 1.061, 1.077, 1.088, - 1.101, 1.085, 1.066, 1.049, 1.035, 1.026, 1.019, 1.013, 1.013, 1.015, 1.021, 1.028, 1.039, 1.052, 1.069, 1.083, - 1.098, 1.081, 1.059, 1.045, 1.031, 1.021, 1.013, 1.007, 1.007, 1.009, 1.014, 1.021, 1.033, 1.046, 1.063, 1.081, - 1.097, 1.076, 1.057, 1.041, 1.027, 1.016, 1.007, 1.004, 1.002, 1.005, 1.009, 1.017, 1.028, 1.043, 1.061, 1.077, - 1.096, 1.075, 1.054, 1.039, 1.025, 1.014, 1.005, 1.001, 1.001, 1.002, 1.006, 1.015, 1.027, 1.041, 1.058, 1.076, - 1.096, 1.074, 1.054, 1.039, 1.025, 1.013, 1.005, 1.001, 1.001, 1.001, 1.006, 1.015, 1.026, 1.041, 1.058, 1.076, - 1.096, 1.075, 1.056, 1.041, 1.026, 1.014, 1.007, 1.003, 1.002, 1.004, 1.008, 1.016, 1.028, 1.041, 1.059, 1.076, - 1.096, 1.079, 1.059, 1.044, 1.029, 1.018, 1.011, 1.007, 1.005, 1.008, 1.012, 1.019, 1.031, 1.044, 1.061, 1.077, - 1.101, 1.084, 1.065, 1.049, 1.035, 1.024, 1.017, 1.011, 1.011, 1.012, 1.018, 1.025, 1.036, 1.051, 1.068, 1.081, - 1.106, 1.092, 1.072, 1.055, 1.042, 1.033, 1.024, 1.019, 1.018, 1.019, 1.025, 1.032, 1.044, 1.058, 1.076, 1.088, - 1.113, 1.097, 1.079, 1.063, 1.049, 1.039, 1.031, 1.025, 1.025, 1.025, 1.031, 1.039, 1.051, 1.065, 1.083, 1.094 - ], - "sigma": 0.00047, - "sigma_Cb": 0.00056 - } - }, - { - "rpi.contrast": - { - "ce_enable": 1, - "gamma_curve": - [ - 0, 0, - 1024, 5040, - 2048, 9338, - 3072, 12356, - 4096, 15312, - 5120, 18051, - 6144, 20790, - 7168, 23193, - 8192, 25744, - 9216, 27942, - 10240, 30035, - 11264, 32005, - 12288, 33975, - 13312, 35815, - 14336, 37600, - 15360, 39168, - 16384, 40642, - 18432, 43379, - 20480, 45749, - 22528, 47753, - 24576, 49621, - 26624, 51253, - 28672, 52698, - 30720, 53796, - 32768, 54876, - 36864, 57012, - 40960, 58656, - 45056, 59954, - 49152, 61183, - 53248, 62355, - 57344, 63419, - 61440, 64476, - 65535, 65535 - ] - } - }, - { - "rpi.ccm": - { - "ccms": [ - { - "ct": 2000, - "ccm": - [ - 1.48716, -0.1877, -0.35079, - -0.48577, 1.55088, -0.03387, - 0.24919, -1.4583, 2.12083 - ] - }, - { - "ct": 2200, - "ccm": - [ - 1.53439, -0.28852, -0.29392, - -0.44748, 1.56295, -0.08907, - 0.23529, -1.30488, 1.99784 - ] - }, - { - "ct": 2400, - "ccm": - [ - 1.57619, -0.36904, -0.25181, - -0.41654, 1.57046, -0.13192, - 0.21678, -1.18352, 1.90786 - ] - }, - { - "ct": 2600, - "ccm": - [ - 1.61348, -0.43497, -0.2198, - -0.39075, 1.5753, -0.1665, - 0.19789, -1.08592, 1.83942 - ] - }, - { - "ct": 2800, - "ccm": - [ - 1.64717, -0.49009, -0.1951, - -0.36881, 1.57852, -0.1952, - 0.18016, -1.00609, 1.78575 - ] - }, - { - "ct": 3000, - "ccm": - [ - 1.67798, -0.53693, -0.17591, - -0.34986, 1.58074, -0.21955, - 0.16406, -0.9398, 1.74261 - ] - }, - { - "ct": 3200, - "ccm": - [ - 1.70647, -0.5773, -0.161, - -0.33332, 1.58235, -0.24056, - 0.14961, -0.88398, 1.70721 - ] - }, - { - "ct": 3400, - "ccm": - [ - 1.73305, -0.61248, -0.14951, - -0.31875, 1.58355, -0.25894, - 0.13671, -0.83642, 1.67769 - ] - }, - { - "ct": 3600, - "ccm": - [ - 1.75802, -0.64343, -0.14077, - -0.30581, 1.5845, -0.27518, - 0.12518, -0.79546, 1.65271 - ] - }, - { - "ct": 4100, - "ccm": - [ - 1.78116, -0.67459, -0.13048, - -0.26859, 1.58692, -0.31929, - 0.11915, -0.77931, 1.64012 - ] - }, - { - "ct": 4600, - "ccm": - [ - 1.83867, -0.73605, -0.12044, - -0.24947, 1.58699, -0.34207, - 0.09949, -0.71041, 1.59842 - ] - }, - { - "ct": 5100, - "ccm": - [ - 1.88967, -0.78455, -0.11744, - -0.23398, 1.58806, -0.36172, - 0.08362, -0.6574, 1.56728 - ] - }, - { - "ct": 5600, - "ccm": - [ - 1.93485, -0.82318, -0.1191, - -0.22108, 1.58973, -0.37892, - 0.07074, -0.61609, 1.54362 - ] - }, - { - "ct": 6100, - "ccm": - [ - 1.97481, -0.85423, -0.12371, - -0.21015, 1.59169, -0.39406, - 0.06021, -0.58353, 1.52536 - ] - }, - { - "ct": 6600, - "ccm": - [ - 2.01029, -0.87946, -0.13017, - -0.20074, 1.59378, -0.4075, - 0.05146, -0.55732, 1.51096 - ] - }, - { - "ct": 7100, - "ccm": - [ - 2.04183, -0.9002, -0.13765, - -0.19255, 1.59586, -0.41944, - 0.04414, -0.53603, 1.49947 - ] - }, - { - "ct": 7600, - "ccm": - [ - 2.07001, -0.91744, -0.14566, - -0.18534, 1.59788, -0.43013, - 0.03791, -0.51841, 1.49013 - ] - }, - { - "ct": 8100, - "ccm": - [ - 2.09534, -0.93195, -0.15388, - -0.17893, 1.59981, -0.43974, - 0.03256, -0.50364, 1.48243 - ] - }, - { - "ct": 8600, - "ccm": - [ - 2.11799, -0.94416, -0.16203, - -0.17324, 1.60161, -0.44836, - 0.02795, -0.4912, 1.47604 - ] - } - ] - } - }, - { - "rpi.sharpen": - { - "threshold": 0.1, - "strength": 1.0, - "limit": 0.18 - } - } - ] -} diff --git a/src/ipa/raspberrypi/data/imx296_mono.json b/src/ipa/raspberrypi/data/imx296_mono.json deleted file mode 100644 index 30965b4b..00000000 --- a/src/ipa/raspberrypi/data/imx296_mono.json +++ /dev/null @@ -1,233 +0,0 @@ -{ - "version": 2.0, - "target": "bcm2835", - "algorithms": [ - { - "rpi.black_level": - { - "black_level": 3840 - } - }, - { - "rpi.dpc": { } - }, - { - "rpi.lux": - { - "reference_shutter_speed": 19184, - "reference_gain": 1.0, - "reference_aperture": 1.0, - "reference_lux": 432, - "reference_Y": 13773 - } - }, - { - "rpi.noise": - { - "reference_constant": 0, - "reference_slope": 2.957 - } - }, - { - "rpi.geq": - { - "offset": 185, - "slope": 0.0105 - } - }, - { - "rpi.sdn": { } - }, - { - "rpi.agc": - { - "metering_modes": - { - "centre-weighted": - { - "weights": [ 3, 3, 3, 2, 2, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0 ] - }, - "spot": - { - "weights": [ 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ] - }, - "matrix": - { - "weights": [ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ] - } - }, - "exposure_modes": - { - "normal": - { - "shutter": [ 100, 10000, 30000, 60000, 120000 ], - "gain": [ 1.0, 2.0, 4.0, 6.0, 6.0 ] - }, - "short": - { - "shutter": [ 100, 5000, 10000, 20000, 120000 ], - "gain": [ 1.0, 2.0, 4.0, 6.0, 6.0 ] - } - }, - "constraint_modes": - { - "normal": [ - { - "bound": "LOWER", - "q_lo": 0.98, - "q_hi": 1.0, - "y_target": - [ - 0, 0.5, - 1000, 0.5 - ] - } - ], - "highlight": [ - { - "bound": "LOWER", - "q_lo": 0.98, - "q_hi": 1.0, - "y_target": - [ - 0, 0.5, - 1000, 0.5 - ] - }, - { - "bound": "UPPER", - "q_lo": 0.98, - "q_hi": 1.0, - "y_target": - [ - 0, 0.8, - 1000, 0.8 - ] - } - ] - }, - "y_target": - [ - 0, 0.16, - 1000, 0.165, - 10000, 0.17 - ] - } - }, - { - "rpi.alsc": - { - "omega": 1.3, - "n_iter": 0, - "luminance_strength": 0.5, - "calibrations_Cr": [ - { - "ct": 4000, - "table": - [ - 2.554, 2.554, 2.541, 2.534, 2.495, 2.506, 2.516, 2.517, 2.518, 2.515, 2.513, 2.495, 2.481, 2.533, 2.533, 2.521, - 2.522, 2.534, 2.539, 2.531, 2.531, 2.506, 2.506, 2.513, 2.513, 2.509, 2.498, 2.496, 2.508, 2.517, 2.521, 2.521, - 2.509, 2.517, 2.534, 2.529, 2.531, 2.521, 2.517, 2.517, 2.515, 2.514, 2.506, 2.499, 2.508, 2.508, 2.521, 2.537, - 2.507, 2.508, 2.517, 2.516, 2.495, 2.487, 2.519, 2.534, 2.535, 2.531, 2.499, 2.494, 2.501, 2.511, 2.526, 2.526, - 2.509, 2.517, 2.507, 2.501, 2.494, 2.519, 2.539, 2.539, 2.537, 2.537, 2.533, 2.499, 2.503, 2.511, 2.529, 2.525, - 2.521, 2.522, 2.476, 2.501, 2.501, 2.539, 2.546, 2.538, 2.531, 2.538, 2.541, 2.531, 2.529, 2.526, 2.529, 2.525, - 2.516, 2.519, 2.469, 2.499, 2.499, 2.543, 2.543, 2.531, 2.528, 2.534, 2.541, 2.535, 2.531, 2.526, 2.531, 2.528, - 2.509, 2.515, 2.465, 2.487, 2.487, 2.539, 2.543, 2.539, 2.533, 2.549, 2.542, 2.531, 2.529, 2.524, 2.532, 2.533, - 2.499, 2.499, 2.475, 2.482, 2.471, 2.509, 2.539, 2.544, 2.543, 2.545, 2.533, 2.498, 2.521, 2.521, 2.537, 2.536, - 2.499, 2.488, 2.488, 2.488, 2.471, 2.462, 2.509, 2.539, 2.539, 2.532, 2.498, 2.498, 2.518, 2.518, 2.539, 2.539, - 2.483, 2.484, 2.488, 2.488, 2.502, 2.496, 2.508, 2.514, 2.518, 2.517, 2.521, 2.518, 2.518, 2.518, 2.525, 2.539, - 2.483, 2.487, 2.478, 2.478, 2.507, 2.509, 2.514, 2.513, 2.514, 2.517, 2.536, 2.559, 2.501, 2.501, 2.503, 2.525 - ] - } - ], - "calibrations_Cb": [ - { - "ct": 4000, - "table": - [ - 2.619, 2.603, 2.599, 2.597, 2.595, 2.594, 2.589, 2.587, 2.586, 2.589, 2.592, 2.597, 2.601, 2.608, 2.621, 2.621, - 2.619, 2.615, 2.603, 2.601, 2.596, 2.595, 2.591, 2.589, 2.589, 2.592, 2.599, 2.593, 2.601, 2.613, 2.622, 2.631, - 2.617, 2.617, 2.612, 2.611, 2.604, 2.598, 2.593, 2.591, 2.592, 2.591, 2.593, 2.595, 2.599, 2.614, 2.623, 2.631, - 2.624, 2.619, 2.615, 2.612, 2.605, 2.602, 2.597, 2.596, 2.592, 2.592, 2.595, 2.599, 2.602, 2.606, 2.619, 2.624, - 2.629, 2.627, 2.627, 2.617, 2.609, 2.598, 2.612, 2.623, 2.615, 2.604, 2.589, 2.595, 2.599, 2.608, 2.611, 2.614, - 2.629, 2.632, 2.637, 2.627, 2.612, 2.612, 2.629, 2.631, 2.628, 2.621, 2.604, 2.597, 2.598, 2.604, 2.609, 2.609, - 2.635, 2.636, 2.642, 2.628, 2.623, 2.623, 2.636, 2.636, 2.634, 2.628, 2.616, 2.599, 2.597, 2.601, 2.603, 2.601, - 2.641, 2.639, 2.646, 2.632, 2.627, 2.625, 2.632, 2.635, 2.634, 2.627, 2.614, 2.596, 2.595, 2.599, 2.599, 2.598, - 2.643, 2.644, 2.651, 2.649, 2.629, 2.617, 2.624, 2.629, 2.625, 2.614, 2.586, 2.599, 2.595, 2.597, 2.592, 2.595, - 2.645, 2.646, 2.649, 2.649, 2.638, 2.624, 2.616, 2.617, 2.609, 2.604, 2.603, 2.603, 2.595, 2.589, 2.587, 2.592, - 2.641, 2.643, 2.649, 2.647, 2.638, 2.618, 2.615, 2.608, 2.602, 2.595, 2.596, 2.595, 2.593, 2.584, 2.581, 2.583, - 2.638, 2.637, 2.647, 2.634, 2.634, 2.618, 2.621, 2.621, 2.611, 2.602, 2.596, 2.583, 2.581, 2.581, 2.576, 2.574 - ] - } - ], - "luminance_lut": - [ - 1.308, 1.293, 1.228, 1.175, 1.139, 1.108, 1.092, 1.082, 1.082, 1.086, 1.097, 1.114, 1.149, 1.199, 1.279, 1.303, - 1.293, 1.249, 1.199, 1.162, 1.136, 1.109, 1.087, 1.077, 1.072, 1.081, 1.095, 1.103, 1.133, 1.172, 1.225, 1.282, - 1.251, 1.212, 1.186, 1.159, 1.129, 1.114, 1.102, 1.088, 1.088, 1.088, 1.095, 1.117, 1.123, 1.158, 1.198, 1.249, - 1.223, 1.192, 1.177, 1.163, 1.147, 1.139, 1.132, 1.112, 1.111, 1.107, 1.113, 1.118, 1.139, 1.155, 1.186, 1.232, - 1.207, 1.186, 1.171, 1.162, 1.168, 1.163, 1.153, 1.138, 1.129, 1.128, 1.132, 1.136, 1.149, 1.167, 1.189, 1.216, - 1.198, 1.186, 1.176, 1.176, 1.177, 1.185, 1.171, 1.157, 1.146, 1.144, 1.146, 1.149, 1.161, 1.181, 1.201, 1.221, - 1.203, 1.181, 1.176, 1.178, 1.191, 1.189, 1.188, 1.174, 1.159, 1.153, 1.158, 1.161, 1.169, 1.185, 1.211, 1.227, - 1.211, 1.179, 1.177, 1.187, 1.194, 1.196, 1.194, 1.187, 1.176, 1.169, 1.171, 1.171, 1.175, 1.189, 1.214, 1.226, - 1.219, 1.182, 1.184, 1.191, 1.195, 1.199, 1.197, 1.194, 1.188, 1.185, 1.179, 1.179, 1.182, 1.194, 1.212, 1.227, - 1.237, 1.192, 1.194, 1.194, 1.198, 1.199, 1.198, 1.197, 1.196, 1.193, 1.189, 1.189, 1.192, 1.203, 1.214, 1.231, - 1.282, 1.199, 1.199, 1.197, 1.199, 1.199, 1.192, 1.193, 1.193, 1.194, 1.196, 1.197, 1.206, 1.216, 1.228, 1.244, - 1.309, 1.236, 1.204, 1.203, 1.202, 1.194, 1.194, 1.188, 1.192, 1.192, 1.199, 1.201, 1.212, 1.221, 1.235, 1.247 - ], - "sigma": 0.005, - "sigma_Cb": 0.005 - } - }, - { - "rpi.contrast": - { - "ce_enable": 1, - "gamma_curve": - [ - 0, 0, - 1024, 5040, - 2048, 9338, - 3072, 12356, - 4096, 15312, - 5120, 18051, - 6144, 20790, - 7168, 23193, - 8192, 25744, - 9216, 27942, - 10240, 30035, - 11264, 32005, - 12288, 33975, - 13312, 35815, - 14336, 37600, - 15360, 39168, - 16384, 40642, - 18432, 43379, - 20480, 45749, - 22528, 47753, - 24576, 49621, - 26624, 51253, - 28672, 52698, - 30720, 53796, - 32768, 54876, - 36864, 57012, - 40960, 58656, - 45056, 59954, - 49152, 61183, - 53248, 62355, - 57344, 63419, - 61440, 64476, - 65535, 65535 - ] - } - }, - { - "rpi.sharpen": - { - "threshold": 0.1, - "strength": 1.0, - "limit": 0.18 - } - } - ] -} diff --git a/src/ipa/raspberrypi/data/imx378.json b/src/ipa/raspberrypi/data/imx378.json deleted file mode 100644 index 8b4ed225..00000000 --- a/src/ipa/raspberrypi/data/imx378.json +++ /dev/null @@ -1,413 +0,0 @@ -{ - "version": 2.0, - "target": "bcm2835", - "algorithms": [ - { - "rpi.black_level": - { - "black_level": 4096 - } - }, - { - "rpi.dpc": { } - }, - { - "rpi.lux": - { - "reference_shutter_speed": 9999, - "reference_gain": 1.95, - "reference_aperture": 1.0, - "reference_lux": 1000, - "reference_Y": 12996 - } - }, - { - "rpi.noise": - { - "reference_constant": 0, - "reference_slope": 2.641 - } - }, - { - "rpi.geq": - { - "offset": 235, - "slope": 0.00902 - } - }, - { - "rpi.sdn": { } - }, - { - "rpi.awb": - { - "priors": [ - { - "lux": 0, - "prior": - [ - 2000, 1.0, - 3000, 0.0, - 13000, 0.0 - ] - }, - { - "lux": 800, - "prior": - [ - 2000, 0.0, - 6000, 2.0, - 13000, 2.0 - ] - }, - { - "lux": 1500, - "prior": - [ - 2000, 0.0, - 4000, 1.0, - 6000, 6.0, - 6500, 7.0, - 7000, 1.0, - 13000, 1.0 - ] - } - ], - "modes": - { - "auto": - { - "lo": 2500, - "hi": 8000 - }, - "incandescent": - { - "lo": 2500, - "hi": 3000 - }, - "tungsten": - { - "lo": 3000, - "hi": 3500 - }, - "fluorescent": - { - "lo": 4000, - "hi": 4700 - }, - "indoor": - { - "lo": 3000, - "hi": 5000 - }, - "daylight": - { - "lo": 5500, - "hi": 6500 - }, - "cloudy": - { - "lo": 7000, - "hi": 8100 - } - }, - "bayes": 1, - "ct_curve": - [ - 2850.0, 0.6361, 0.3911, - 3550.0, 0.5386, 0.5077, - 4500.0, 0.4472, 0.6171, - 5600.0, 0.3906, 0.6848, - 8000.0, 0.3412, 0.7441 - ], - "sensitivity_r": 1.0, - "sensitivity_b": 1.0, - "transverse_pos": 0.01667, - "transverse_neg": 0.01195 - } - }, - { - "rpi.agc": - { - "metering_modes": - { - "centre-weighted": - { - "weights": [ 3, 3, 3, 2, 2, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0 ] - }, - "spot": - { - "weights": [ 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ] - }, - "matrix": - { - "weights": [ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ] - } - }, - "exposure_modes": - { - "normal": - { - "shutter": [ 100, 10000, 30000, 60000, 120000 ], - "gain": [ 1.0, 2.0, 4.0, 6.0, 8.0 ] - }, - "short": - { - "shutter": [ 100, 5000, 10000, 20000, 120000 ], - "gain": [ 1.0, 2.0, 4.0, 6.0, 8.0 ] - } - }, - "constraint_modes": - { - "normal": [ - { - "bound": "LOWER", - "q_lo": 0.98, - "q_hi": 1.0, - "y_target": - [ - 0, 0.5, - 1000, 0.5 - ] - } - ], - "highlight": [ - { - "bound": "LOWER", - "q_lo": 0.98, - "q_hi": 1.0, - "y_target": - [ - 0, 0.5, - 1000, 0.5 - ] - }, - { - "bound": "UPPER", - "q_lo": 0.98, - "q_hi": 1.0, - "y_target": - [ - 0, 0.8, - 1000, 0.8 - ] - } - ] - }, - "y_target": - [ - 0, 0.16, - 1000, 0.165, - 10000, 0.17 - ] - } - }, - { - "rpi.alsc": - { - "omega": 1.3, - "n_iter": 100, - "luminance_strength": 0.5, - "calibrations_Cr": [ - { - "ct": 2800, - "table": - [ - 1.604, 1.601, 1.593, 1.581, 1.568, 1.561, 1.561, 1.561, 1.561, 1.567, 1.582, 1.596, 1.609, 1.622, 1.632, 1.636, - 1.601, 1.594, 1.586, 1.571, 1.555, 1.546, 1.543, 1.543, 1.547, 1.555, 1.572, 1.584, 1.599, 1.614, 1.625, 1.632, - 1.599, 1.586, 1.571, 1.555, 1.542, 1.528, 1.518, 1.518, 1.523, 1.537, 1.555, 1.572, 1.589, 1.607, 1.622, 1.629, - 1.597, 1.579, 1.561, 1.542, 1.528, 1.512, 1.493, 1.493, 1.499, 1.523, 1.537, 1.563, 1.582, 1.601, 1.619, 1.629, - 1.597, 1.577, 1.557, 1.535, 1.512, 1.493, 1.481, 1.479, 1.492, 1.499, 1.524, 1.555, 1.578, 1.599, 1.619, 1.629, - 1.597, 1.577, 1.557, 1.534, 1.508, 1.483, 1.476, 1.476, 1.481, 1.496, 1.522, 1.554, 1.578, 1.599, 1.619, 1.629, - 1.597, 1.578, 1.557, 1.534, 1.508, 1.483, 1.481, 1.479, 1.481, 1.496, 1.522, 1.554, 1.579, 1.601, 1.619, 1.631, - 1.597, 1.581, 1.562, 1.539, 1.517, 1.504, 1.483, 1.481, 1.496, 1.511, 1.531, 1.561, 1.585, 1.607, 1.623, 1.632, - 1.601, 1.589, 1.569, 1.554, 1.539, 1.517, 1.504, 1.504, 1.511, 1.531, 1.553, 1.573, 1.596, 1.614, 1.629, 1.636, - 1.609, 1.601, 1.586, 1.569, 1.554, 1.542, 1.535, 1.535, 1.541, 1.553, 1.573, 1.592, 1.608, 1.625, 1.637, 1.645, - 1.617, 1.611, 1.601, 1.586, 1.574, 1.565, 1.564, 1.564, 1.571, 1.579, 1.592, 1.608, 1.622, 1.637, 1.646, 1.654, - 1.619, 1.617, 1.611, 1.601, 1.588, 1.585, 1.585, 1.585, 1.588, 1.592, 1.607, 1.622, 1.637, 1.645, 1.654, 1.655 - ] - }, - { - "ct": 5500, - "table": - [ - 2.664, 2.658, 2.645, 2.629, 2.602, 2.602, 2.602, 2.606, 2.617, 2.628, 2.649, 2.677, 2.699, 2.722, 2.736, 2.747, - 2.658, 2.653, 2.629, 2.605, 2.576, 2.575, 2.577, 2.592, 2.606, 2.618, 2.629, 2.651, 2.678, 2.707, 2.727, 2.741, - 2.649, 2.631, 2.605, 2.576, 2.563, 2.552, 2.552, 2.557, 2.577, 2.604, 2.619, 2.641, 2.669, 2.698, 2.721, 2.741, - 2.643, 2.613, 2.583, 2.563, 2.552, 2.531, 2.527, 2.527, 2.551, 2.577, 2.604, 2.638, 2.665, 2.694, 2.721, 2.741, - 2.643, 2.606, 2.575, 2.558, 2.531, 2.516, 2.504, 2.516, 2.527, 2.551, 2.596, 2.635, 2.665, 2.694, 2.721, 2.741, - 2.643, 2.606, 2.575, 2.558, 2.531, 2.503, 2.501, 2.502, 2.522, 2.551, 2.592, 2.635, 2.669, 2.696, 2.727, 2.744, - 2.648, 2.611, 2.579, 2.558, 2.532, 2.511, 2.502, 2.511, 2.522, 2.552, 2.592, 2.642, 2.673, 2.702, 2.731, 2.752, - 2.648, 2.619, 2.589, 2.571, 2.556, 2.532, 2.519, 2.522, 2.552, 2.568, 2.605, 2.648, 2.683, 2.715, 2.743, 2.758, - 2.659, 2.637, 2.613, 2.589, 2.571, 2.556, 2.555, 2.555, 2.568, 2.605, 2.641, 2.671, 2.699, 2.729, 2.758, 2.776, - 2.679, 2.665, 2.637, 2.613, 2.602, 2.599, 2.599, 2.606, 2.619, 2.641, 2.671, 2.698, 2.723, 2.754, 2.776, 2.787, - 2.695, 2.684, 2.671, 2.646, 2.636, 2.636, 2.641, 2.648, 2.661, 2.681, 2.698, 2.723, 2.751, 2.776, 2.788, 2.803, - 2.702, 2.699, 2.684, 2.671, 2.664, 2.664, 2.664, 2.668, 2.681, 2.698, 2.723, 2.751, 2.773, 2.788, 2.803, 2.805 - ] - } - ], - "calibrations_Cb": [ - { - "ct": 2800, - "table": - [ - 2.876, 2.868, 2.863, 2.851, 2.846, 2.846, 2.847, 2.851, 2.851, 2.857, 2.867, 2.875, 2.889, 2.899, 2.913, 2.926, - 2.863, 2.861, 2.856, 2.846, 2.846, 2.847, 2.848, 2.851, 2.857, 2.859, 2.875, 2.882, 2.886, 2.896, 2.909, 2.917, - 2.861, 2.856, 2.846, 2.841, 2.841, 2.855, 2.867, 2.875, 2.888, 2.888, 2.885, 2.883, 2.886, 2.889, 2.901, 2.913, - 2.858, 2.851, 2.846, 2.846, 2.855, 2.867, 2.884, 2.895, 2.902, 2.902, 2.901, 2.891, 2.891, 2.894, 2.901, 2.909, - 2.858, 2.851, 2.846, 2.846, 2.867, 2.884, 2.895, 2.902, 2.909, 2.915, 2.911, 2.901, 2.895, 2.898, 2.904, 2.909, - 2.858, 2.851, 2.849, 2.853, 2.874, 2.888, 2.901, 2.909, 2.917, 2.922, 2.917, 2.911, 2.901, 2.899, 2.905, 2.908, - 2.861, 2.855, 2.853, 2.855, 2.874, 2.888, 2.901, 2.913, 2.918, 2.922, 2.921, 2.911, 2.901, 2.901, 2.907, 2.908, - 2.862, 2.859, 2.855, 2.856, 2.872, 2.885, 2.899, 2.906, 2.915, 2.917, 2.911, 2.907, 2.907, 2.907, 2.908, 2.909, - 2.863, 2.863, 2.859, 2.864, 2.871, 2.881, 2.885, 2.899, 2.905, 2.905, 2.904, 2.904, 2.907, 2.909, 2.913, 2.913, - 2.866, 2.865, 2.865, 2.867, 2.868, 2.872, 2.881, 2.885, 2.889, 2.894, 2.895, 2.902, 2.906, 2.913, 2.914, 2.917, - 2.875, 2.875, 2.871, 2.871, 2.871, 2.871, 2.869, 2.869, 2.878, 2.889, 2.894, 2.895, 2.906, 2.914, 2.917, 2.921, - 2.882, 2.879, 2.876, 2.874, 2.871, 2.871, 2.869, 2.869, 2.869, 2.878, 2.891, 2.894, 2.905, 2.914, 2.919, 2.921 - ] - }, - { - "ct": 5500, - "table": - [ - 1.488, 1.488, 1.488, 1.488, 1.491, 1.492, 1.492, 1.491, 1.491, 1.491, 1.492, 1.495, 1.497, 1.499, 1.499, 1.503, - 1.482, 1.485, 1.485, 1.487, 1.489, 1.492, 1.492, 1.492, 1.492, 1.492, 1.494, 1.494, 1.492, 1.491, 1.493, 1.494, - 1.482, 1.482, 1.484, 1.485, 1.487, 1.492, 1.496, 1.498, 1.499, 1.498, 1.494, 1.492, 1.491, 1.491, 1.491, 1.491, - 1.481, 1.481, 1.482, 1.485, 1.491, 1.496, 1.498, 1.499, 1.501, 1.499, 1.498, 1.493, 1.491, 1.488, 1.488, 1.488, - 1.481, 1.481, 1.481, 1.483, 1.491, 1.497, 1.498, 1.499, 1.501, 1.499, 1.498, 1.492, 1.488, 1.485, 1.483, 1.483, - 1.479, 1.479, 1.481, 1.482, 1.489, 1.495, 1.497, 1.498, 1.499, 1.499, 1.495, 1.492, 1.485, 1.482, 1.482, 1.481, - 1.479, 1.479, 1.479, 1.481, 1.489, 1.494, 1.496, 1.497, 1.497, 1.496, 1.495, 1.489, 1.482, 1.481, 1.479, 1.477, - 1.478, 1.478, 1.479, 1.481, 1.487, 1.491, 1.494, 1.496, 1.496, 1.495, 1.492, 1.487, 1.482, 1.479, 1.478, 1.476, - 1.478, 1.478, 1.479, 1.482, 1.486, 1.488, 1.491, 1.493, 1.493, 1.492, 1.487, 1.484, 1.481, 1.479, 1.476, 1.476, - 1.477, 1.479, 1.481, 1.483, 1.485, 1.486, 1.488, 1.488, 1.487, 1.487, 1.484, 1.483, 1.481, 1.479, 1.476, 1.476, - 1.477, 1.479, 1.482, 1.483, 1.484, 1.485, 1.484, 1.482, 1.482, 1.484, 1.483, 1.482, 1.481, 1.479, 1.477, 1.476, - 1.477, 1.479, 1.482, 1.483, 1.484, 1.484, 1.482, 1.482, 1.482, 1.482, 1.482, 1.481, 1.479, 1.479, 1.479, 1.479 - ] - } - ], - "luminance_lut": - [ - 2.764, 2.654, 2.321, 2.043, 1.768, 1.594, 1.558, 1.558, 1.558, 1.568, 1.661, 1.904, 2.193, 2.497, 2.888, 3.043, - 2.654, 2.373, 2.049, 1.819, 1.569, 1.446, 1.381, 1.356, 1.356, 1.403, 1.501, 1.679, 1.939, 2.218, 2.586, 2.888, - 2.376, 2.154, 1.819, 1.569, 1.438, 1.301, 1.246, 1.224, 1.224, 1.263, 1.349, 1.501, 1.679, 1.985, 2.359, 2.609, - 2.267, 1.987, 1.662, 1.438, 1.301, 1.235, 1.132, 1.105, 1.105, 1.164, 1.263, 1.349, 1.528, 1.808, 2.184, 2.491, - 2.218, 1.876, 1.568, 1.367, 1.235, 1.132, 1.087, 1.022, 1.023, 1.104, 1.164, 1.278, 1.439, 1.695, 2.066, 2.429, - 2.218, 1.832, 1.533, 1.341, 1.206, 1.089, 1.013, 1.002, 1.013, 1.026, 1.122, 1.246, 1.399, 1.642, 2.004, 2.426, - 2.218, 1.832, 1.533, 1.341, 1.206, 1.089, 1.011, 1.001, 1.009, 1.026, 1.122, 1.246, 1.399, 1.642, 2.004, 2.426, - 2.224, 1.896, 1.584, 1.382, 1.248, 1.147, 1.088, 1.016, 1.026, 1.118, 1.168, 1.283, 1.444, 1.697, 2.066, 2.428, - 2.292, 2.019, 1.689, 1.462, 1.322, 1.247, 1.147, 1.118, 1.118, 1.168, 1.275, 1.358, 1.532, 1.809, 2.189, 2.491, - 2.444, 2.204, 1.856, 1.606, 1.462, 1.322, 1.257, 1.234, 1.234, 1.275, 1.358, 1.516, 1.686, 1.993, 2.371, 2.622, - 2.748, 2.444, 2.108, 1.856, 1.606, 1.476, 1.399, 1.376, 1.376, 1.422, 1.516, 1.686, 1.968, 2.238, 2.611, 2.935, - 2.862, 2.748, 2.395, 2.099, 1.811, 1.621, 1.582, 1.582, 1.582, 1.592, 1.677, 1.919, 2.223, 2.534, 2.935, 3.078 - ], - "sigma": 0.00428, - "sigma_Cb": 0.00363 - } - }, - { - "rpi.contrast": - { - "ce_enable": 1, - "gamma_curve": - [ - 0, 0, - 1024, 5040, - 2048, 9338, - 3072, 12356, - 4096, 15312, - 5120, 18051, - 6144, 20790, - 7168, 23193, - 8192, 25744, - 9216, 27942, - 10240, 30035, - 11264, 32005, - 12288, 33975, - 13312, 35815, - 14336, 37600, - 15360, 39168, - 16384, 40642, - 18432, 43379, - 20480, 45749, - 22528, 47753, - 24576, 49621, - 26624, 51253, - 28672, 52698, - 30720, 53796, - 32768, 54876, - 36864, 57012, - 40960, 58656, - 45056, 59954, - 49152, 61183, - 53248, 62355, - 57344, 63419, - 61440, 64476, - 65535, 65535 - ] - } - }, - { - "rpi.ccm": - { - "ccms": [ - { - "ct": 2850, - "ccm": - [ - 1.42601, -0.20537, -0.22063, - -0.47682, 1.81987, -0.34305, - 0.01854, -0.86036, 1.84181 - ] - }, - { - "ct": 2900, - "ccm": - [ - 1.29755, 0.04602, -0.34356, - -0.41491, 1.73477, -0.31987, - -0.01345, -0.97115, 1.98459 - ] - }, - { - "ct": 3550, - "ccm": - [ - 1.49811, -0.33412, -0.16398, - -0.40869, 1.72995, -0.32127, - -0.01924, -0.62181, 1.64105 - ] - }, - { - "ct": 4500, - "ccm": - [ - 1.47015, -0.29229, -0.17786, - -0.36561, 1.88919, -0.52358, - -0.03552, -0.56717, 1.60269 - ] - }, - { - "ct": 5600, - "ccm": - [ - 1.60962, -0.47434, -0.13528, - -0.32701, 1.73797, -0.41096, - -0.07626, -0.40171, 1.47796 - ] - }, - { - "ct": 8000, - "ccm": - [ - 1.54642, -0.20396, -0.34246, - -0.31748, 2.22559, -0.90811, - -0.10035, -0.65877, 1.75912 - ] - } - ] - } - }, - { - "rpi.sharpen": { } - } - ] -} \ No newline at end of file diff --git a/src/ipa/raspberrypi/data/imx477.json b/src/ipa/raspberrypi/data/imx477.json deleted file mode 100644 index daffc268..00000000 --- a/src/ipa/raspberrypi/data/imx477.json +++ /dev/null @@ -1,518 +0,0 @@ -{ - "version": 2.0, - "target": "bcm2835", - "algorithms": [ - { - "rpi.black_level": - { - "black_level": 4096 - } - }, - { - "rpi.dpc": { } - }, - { - "rpi.lux": - { - "reference_shutter_speed": 27242, - "reference_gain": 1.0, - "reference_aperture": 1.0, - "reference_lux": 830, - "reference_Y": 17755 - } - }, - { - "rpi.noise": - { - "reference_constant": 0, - "reference_slope": 2.767 - } - }, - { - "rpi.geq": - { - "offset": 204, - "slope": 0.01078 - } - }, - { - "rpi.sdn": { } - }, - { - "rpi.awb": - { - "priors": [ - { - "lux": 0, - "prior": - [ - 2000, 1.0, - 3000, 0.0, - 13000, 0.0 - ] - }, - { - "lux": 800, - "prior": - [ - 2000, 0.0, - 6000, 2.0, - 13000, 2.0 - ] - }, - { - "lux": 1500, - "prior": - [ - 2000, 0.0, - 4000, 1.0, - 6000, 6.0, - 6500, 7.0, - 7000, 1.0, - 13000, 1.0 - ] - } - ], - "modes": - { - "auto": - { - "lo": 2500, - "hi": 8000 - }, - "incandescent": - { - "lo": 2500, - "hi": 3000 - }, - "tungsten": - { - "lo": 3000, - "hi": 3500 - }, - "fluorescent": - { - "lo": 4000, - "hi": 4700 - }, - "indoor": - { - "lo": 3000, - "hi": 5000 - }, - "daylight": - { - "lo": 5500, - "hi": 6500 - }, - "cloudy": - { - "lo": 7000, - "hi": 8600 - } - }, - "bayes": 1, - "ct_curve": - [ - 2360.0, 0.6009, 0.3093, - 2848.0, 0.5071, 0.4000, - 2903.0, 0.4905, 0.4392, - 3628.0, 0.4261, 0.5564, - 3643.0, 0.4228, 0.5623, - 4660.0, 0.3529, 0.6800, - 5579.0, 0.3227, 0.7000, - 6125.0, 0.3129, 0.7100, - 6671.0, 0.3065, 0.7200, - 7217.0, 0.3014, 0.7300, - 7763.0, 0.2950, 0.7400, - 9505.0, 0.2524, 0.7856 - ], - "sensitivity_r": 1.05, - "sensitivity_b": 1.05, - "transverse_pos": 0.0238, - "transverse_neg": 0.04429 - } - }, - { - "rpi.agc": - { - "metering_modes": - { - "centre-weighted": - { - "weights": [ 3, 3, 3, 2, 2, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0 ] - }, - "spot": - { - "weights": [ 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ] - }, - "matrix": - { - "weights": [ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ] - } - }, - "exposure_modes": - { - "normal": - { - "shutter": [ 100, 10000, 30000, 60000, 66666 ], - "gain": [ 1.0, 2.0, 4.0, 6.0, 8.0 ] - }, - "short": - { - "shutter": [ 100, 5000, 10000, 20000, 33333 ], - "gain": [ 1.0, 2.0, 4.0, 6.0, 8.0 ] - }, - "long": - { - "shutter": [ 100, 10000, 30000, 60000, 120000 ], - "gain": [ 1.0, 2.0, 4.0, 6.0, 12.0 ] - } - }, - "constraint_modes": - { - "normal": [ - { - "bound": "LOWER", - "q_lo": 0.98, - "q_hi": 1.0, - "y_target": - [ - 0, 0.3, - 1000, 0.3 - ] - } - ], - "highlight": [ - { - "bound": "LOWER", - "q_lo": 0.98, - "q_hi": 1.0, - "y_target": - [ - 0, 0.3, - 1000, 0.3 - ] - }, - { - "bound": "UPPER", - "q_lo": 0.98, - "q_hi": 1.0, - "y_target": - [ - 0, 0.8, - 1000, 0.8 - ] - } - ], - "shadows": [ - { - "bound": "LOWER", - "q_lo": 0.0, - "q_hi": 0.5, - "y_target": - [ - 0, 0.17, - 1000, 0.17 - ] - } - ] - }, - "y_target": - [ - 0, 0.16, - 1000, 0.165, - 10000, 0.17 - ] - } - }, - { - "rpi.alsc": - { - "omega": 1.3, - "n_iter": 100, - "luminance_strength": 0.5, - "calibrations_Cr": [ - { - "ct": 2960, - "table": - [ - 2.088, 2.086, 2.082, 2.081, 2.077, 2.071, 2.068, 2.068, 2.072, 2.073, 2.075, 2.078, 2.084, 2.092, 2.095, 2.098, - 2.086, 2.084, 2.079, 2.078, 2.075, 2.068, 2.064, 2.063, 2.068, 2.071, 2.072, 2.075, 2.081, 2.089, 2.092, 2.094, - 2.083, 2.081, 2.077, 2.072, 2.069, 2.062, 2.059, 2.059, 2.063, 2.067, 2.069, 2.072, 2.079, 2.088, 2.089, 2.089, - 2.081, 2.077, 2.072, 2.068, 2.065, 2.058, 2.055, 2.054, 2.057, 2.062, 2.066, 2.069, 2.077, 2.084, 2.086, 2.086, - 2.078, 2.075, 2.069, 2.065, 2.061, 2.055, 2.052, 2.049, 2.051, 2.056, 2.062, 2.065, 2.072, 2.079, 2.081, 2.079, - 2.079, 2.075, 2.069, 2.064, 2.061, 2.053, 2.049, 2.046, 2.049, 2.051, 2.057, 2.062, 2.069, 2.075, 2.077, 2.075, - 2.082, 2.079, 2.072, 2.065, 2.061, 2.054, 2.049, 2.047, 2.049, 2.051, 2.056, 2.061, 2.066, 2.073, 2.073, 2.069, - 2.086, 2.082, 2.075, 2.068, 2.062, 2.054, 2.051, 2.049, 2.051, 2.052, 2.056, 2.061, 2.066, 2.073, 2.073, 2.072, - 2.088, 2.086, 2.079, 2.074, 2.066, 2.057, 2.051, 2.051, 2.054, 2.055, 2.056, 2.061, 2.067, 2.072, 2.073, 2.072, - 2.091, 2.087, 2.079, 2.075, 2.068, 2.057, 2.052, 2.052, 2.056, 2.055, 2.055, 2.059, 2.066, 2.072, 2.072, 2.072, - 2.093, 2.088, 2.081, 2.077, 2.069, 2.059, 2.054, 2.054, 2.057, 2.056, 2.056, 2.058, 2.066, 2.072, 2.073, 2.073, - 2.095, 2.091, 2.084, 2.078, 2.075, 2.067, 2.057, 2.057, 2.059, 2.059, 2.058, 2.059, 2.068, 2.073, 2.075, 2.078 - ] - }, - { - "ct": 4850, - "table": - [ - 2.973, 2.968, 2.956, 2.943, 2.941, 2.932, 2.923, 2.921, 2.924, 2.929, 2.931, 2.939, 2.953, 2.965, 2.966, 2.976, - 2.969, 2.962, 2.951, 2.941, 2.934, 2.928, 2.919, 2.918, 2.919, 2.923, 2.927, 2.933, 2.945, 2.957, 2.962, 2.962, - 2.964, 2.956, 2.944, 2.932, 2.929, 2.924, 2.915, 2.914, 2.915, 2.919, 2.924, 2.928, 2.941, 2.952, 2.958, 2.959, - 2.957, 2.951, 2.939, 2.928, 2.924, 2.919, 2.913, 2.911, 2.911, 2.915, 2.919, 2.925, 2.936, 2.947, 2.952, 2.953, - 2.954, 2.947, 2.935, 2.924, 2.919, 2.915, 2.908, 2.906, 2.906, 2.907, 2.914, 2.921, 2.932, 2.941, 2.943, 2.942, - 2.953, 2.946, 2.932, 2.921, 2.916, 2.911, 2.904, 2.902, 2.901, 2.904, 2.909, 2.919, 2.926, 2.937, 2.939, 2.939, - 2.953, 2.947, 2.932, 2.918, 2.915, 2.909, 2.903, 2.901, 2.901, 2.906, 2.911, 2.918, 2.924, 2.936, 2.936, 2.932, - 2.956, 2.948, 2.934, 2.919, 2.916, 2.908, 2.903, 2.901, 2.902, 2.907, 2.909, 2.917, 2.926, 2.936, 2.939, 2.939, - 2.957, 2.951, 2.936, 2.923, 2.917, 2.907, 2.904, 2.901, 2.902, 2.908, 2.911, 2.919, 2.929, 2.939, 2.942, 2.942, - 2.961, 2.951, 2.936, 2.922, 2.918, 2.906, 2.904, 2.901, 2.901, 2.907, 2.911, 2.921, 2.931, 2.941, 2.942, 2.944, - 2.964, 2.954, 2.936, 2.924, 2.918, 2.909, 2.905, 2.905, 2.905, 2.907, 2.912, 2.923, 2.933, 2.942, 2.944, 2.944, - 2.964, 2.958, 2.943, 2.927, 2.921, 2.914, 2.909, 2.907, 2.907, 2.912, 2.916, 2.928, 2.936, 2.944, 2.947, 2.952 - ] - }, - { - "ct": 5930, - "table": - [ - 3.312, 3.308, 3.301, 3.294, 3.288, 3.277, 3.268, 3.261, 3.259, 3.261, 3.267, 3.273, 3.285, 3.301, 3.303, 3.312, - 3.308, 3.304, 3.294, 3.291, 3.283, 3.271, 3.263, 3.259, 3.257, 3.258, 3.261, 3.268, 3.278, 3.293, 3.299, 3.299, - 3.302, 3.296, 3.288, 3.282, 3.276, 3.267, 3.259, 3.254, 3.252, 3.253, 3.256, 3.261, 3.273, 3.289, 3.292, 3.292, - 3.296, 3.289, 3.282, 3.276, 3.269, 3.263, 3.256, 3.251, 3.248, 3.249, 3.251, 3.257, 3.268, 3.279, 3.284, 3.284, - 3.292, 3.285, 3.279, 3.271, 3.264, 3.257, 3.249, 3.243, 3.241, 3.241, 3.246, 3.252, 3.261, 3.274, 3.275, 3.273, - 3.291, 3.285, 3.276, 3.268, 3.259, 3.251, 3.242, 3.239, 3.236, 3.238, 3.244, 3.248, 3.258, 3.268, 3.269, 3.265, - 3.294, 3.288, 3.275, 3.266, 3.257, 3.248, 3.239, 3.238, 3.237, 3.238, 3.243, 3.246, 3.255, 3.264, 3.264, 3.257, - 3.297, 3.293, 3.279, 3.268, 3.258, 3.249, 3.238, 3.237, 3.239, 3.239, 3.243, 3.245, 3.255, 3.264, 3.264, 3.263, - 3.301, 3.295, 3.281, 3.271, 3.259, 3.248, 3.237, 3.237, 3.239, 3.241, 3.243, 3.246, 3.257, 3.265, 3.266, 3.264, - 3.306, 3.295, 3.279, 3.271, 3.261, 3.247, 3.235, 3.234, 3.239, 3.239, 3.243, 3.247, 3.258, 3.265, 3.265, 3.264, - 3.308, 3.297, 3.279, 3.272, 3.261, 3.249, 3.239, 3.239, 3.241, 3.243, 3.245, 3.248, 3.261, 3.265, 3.266, 3.265, - 3.309, 3.301, 3.286, 3.276, 3.267, 3.256, 3.246, 3.242, 3.244, 3.244, 3.249, 3.253, 3.263, 3.267, 3.271, 3.274 - ] - } - ], - "calibrations_Cb": [ - { - "ct": 2960, - "table": - [ - 2.133, 2.134, 2.139, 2.143, 2.148, 2.155, 2.158, 2.158, 2.158, 2.161, 2.161, 2.162, 2.159, 2.156, 2.152, 2.151, - 2.132, 2.133, 2.135, 2.142, 2.147, 2.153, 2.158, 2.158, 2.158, 2.158, 2.159, 2.159, 2.157, 2.154, 2.151, 2.148, - 2.133, 2.133, 2.135, 2.142, 2.149, 2.154, 2.158, 2.158, 2.157, 2.156, 2.158, 2.157, 2.155, 2.153, 2.148, 2.146, - 2.133, 2.133, 2.138, 2.145, 2.149, 2.154, 2.158, 2.159, 2.158, 2.155, 2.157, 2.156, 2.153, 2.149, 2.146, 2.144, - 2.133, 2.134, 2.139, 2.146, 2.149, 2.154, 2.158, 2.159, 2.159, 2.156, 2.154, 2.154, 2.149, 2.145, 2.143, 2.139, - 2.135, 2.135, 2.139, 2.146, 2.151, 2.155, 2.158, 2.159, 2.158, 2.156, 2.153, 2.151, 2.146, 2.143, 2.139, 2.136, - 2.135, 2.135, 2.138, 2.145, 2.151, 2.154, 2.157, 2.158, 2.157, 2.156, 2.153, 2.151, 2.147, 2.143, 2.141, 2.137, - 2.135, 2.134, 2.135, 2.141, 2.149, 2.154, 2.157, 2.157, 2.157, 2.157, 2.157, 2.153, 2.149, 2.146, 2.142, 2.139, - 2.132, 2.133, 2.135, 2.139, 2.148, 2.153, 2.158, 2.159, 2.159, 2.161, 2.161, 2.157, 2.154, 2.149, 2.144, 2.141, - 2.132, 2.133, 2.135, 2.141, 2.149, 2.155, 2.161, 2.161, 2.162, 2.162, 2.163, 2.159, 2.154, 2.149, 2.144, 2.138, - 2.136, 2.136, 2.137, 2.143, 2.149, 2.156, 2.162, 2.163, 2.162, 2.163, 2.164, 2.161, 2.157, 2.152, 2.146, 2.138, - 2.137, 2.137, 2.141, 2.147, 2.152, 2.157, 2.162, 2.162, 2.159, 2.161, 2.162, 2.162, 2.157, 2.152, 2.148, 2.148 - ] - }, - { - "ct": 4850, - "table": - [ - 1.463, 1.464, 1.471, 1.478, 1.479, 1.483, 1.484, 1.486, 1.486, 1.484, 1.483, 1.481, 1.478, 1.475, 1.471, 1.468, - 1.463, 1.463, 1.468, 1.476, 1.479, 1.482, 1.484, 1.487, 1.486, 1.484, 1.483, 1.482, 1.478, 1.473, 1.469, 1.468, - 1.463, 1.464, 1.468, 1.476, 1.479, 1.483, 1.484, 1.486, 1.486, 1.485, 1.484, 1.482, 1.477, 1.473, 1.469, 1.468, - 1.463, 1.464, 1.469, 1.477, 1.481, 1.483, 1.485, 1.487, 1.487, 1.485, 1.485, 1.482, 1.478, 1.474, 1.469, 1.468, - 1.465, 1.465, 1.471, 1.478, 1.481, 1.484, 1.486, 1.488, 1.488, 1.487, 1.485, 1.482, 1.477, 1.472, 1.468, 1.467, - 1.465, 1.466, 1.472, 1.479, 1.482, 1.485, 1.486, 1.488, 1.488, 1.486, 1.484, 1.479, 1.475, 1.472, 1.468, 1.466, - 1.466, 1.466, 1.472, 1.478, 1.482, 1.484, 1.485, 1.488, 1.487, 1.485, 1.483, 1.479, 1.475, 1.472, 1.469, 1.468, - 1.465, 1.466, 1.469, 1.476, 1.481, 1.485, 1.485, 1.486, 1.486, 1.485, 1.483, 1.479, 1.477, 1.474, 1.471, 1.469, - 1.464, 1.465, 1.469, 1.476, 1.481, 1.484, 1.485, 1.487, 1.487, 1.486, 1.485, 1.481, 1.478, 1.475, 1.471, 1.469, - 1.463, 1.464, 1.469, 1.477, 1.481, 1.485, 1.485, 1.488, 1.488, 1.487, 1.486, 1.481, 1.478, 1.475, 1.471, 1.468, - 1.464, 1.465, 1.471, 1.478, 1.482, 1.486, 1.486, 1.488, 1.488, 1.487, 1.486, 1.481, 1.478, 1.475, 1.472, 1.468, - 1.465, 1.466, 1.472, 1.481, 1.483, 1.487, 1.487, 1.488, 1.488, 1.486, 1.485, 1.481, 1.479, 1.476, 1.473, 1.472 - ] - }, - { - "ct": 5930, - "table": - [ - 1.443, 1.444, 1.448, 1.453, 1.459, 1.463, 1.465, 1.467, 1.469, 1.469, 1.467, 1.466, 1.462, 1.457, 1.454, 1.451, - 1.443, 1.444, 1.445, 1.451, 1.459, 1.463, 1.465, 1.467, 1.469, 1.469, 1.467, 1.465, 1.461, 1.456, 1.452, 1.451, - 1.444, 1.444, 1.445, 1.451, 1.459, 1.463, 1.466, 1.468, 1.469, 1.469, 1.467, 1.465, 1.461, 1.456, 1.452, 1.449, - 1.444, 1.444, 1.447, 1.452, 1.459, 1.464, 1.467, 1.469, 1.471, 1.469, 1.467, 1.466, 1.461, 1.456, 1.452, 1.449, - 1.444, 1.445, 1.448, 1.452, 1.459, 1.465, 1.469, 1.471, 1.471, 1.471, 1.468, 1.465, 1.461, 1.455, 1.451, 1.449, - 1.445, 1.446, 1.449, 1.453, 1.461, 1.466, 1.469, 1.471, 1.472, 1.469, 1.467, 1.465, 1.459, 1.455, 1.451, 1.447, - 1.446, 1.446, 1.449, 1.453, 1.461, 1.466, 1.469, 1.469, 1.469, 1.469, 1.467, 1.465, 1.459, 1.455, 1.452, 1.449, - 1.446, 1.446, 1.447, 1.451, 1.459, 1.466, 1.469, 1.469, 1.469, 1.469, 1.467, 1.465, 1.461, 1.457, 1.454, 1.451, - 1.444, 1.444, 1.447, 1.451, 1.459, 1.466, 1.469, 1.469, 1.471, 1.471, 1.468, 1.466, 1.462, 1.458, 1.454, 1.452, - 1.444, 1.444, 1.448, 1.453, 1.459, 1.466, 1.469, 1.471, 1.472, 1.472, 1.468, 1.466, 1.462, 1.458, 1.454, 1.449, - 1.446, 1.447, 1.449, 1.454, 1.461, 1.466, 1.471, 1.471, 1.471, 1.471, 1.468, 1.466, 1.462, 1.459, 1.455, 1.449, - 1.447, 1.447, 1.452, 1.457, 1.462, 1.468, 1.472, 1.472, 1.471, 1.471, 1.468, 1.466, 1.462, 1.459, 1.456, 1.455 - ] - } - ], - "luminance_lut": - [ - 1.548, 1.499, 1.387, 1.289, 1.223, 1.183, 1.164, 1.154, 1.153, 1.169, 1.211, 1.265, 1.345, 1.448, 1.581, 1.619, - 1.513, 1.412, 1.307, 1.228, 1.169, 1.129, 1.105, 1.098, 1.103, 1.127, 1.157, 1.209, 1.272, 1.361, 1.481, 1.583, - 1.449, 1.365, 1.257, 1.175, 1.124, 1.085, 1.062, 1.054, 1.059, 1.079, 1.113, 1.151, 1.211, 1.293, 1.407, 1.488, - 1.424, 1.324, 1.222, 1.139, 1.089, 1.056, 1.034, 1.031, 1.034, 1.049, 1.075, 1.115, 1.164, 1.241, 1.351, 1.446, - 1.412, 1.297, 1.203, 1.119, 1.069, 1.039, 1.021, 1.016, 1.022, 1.032, 1.052, 1.086, 1.135, 1.212, 1.321, 1.439, - 1.406, 1.287, 1.195, 1.115, 1.059, 1.028, 1.014, 1.012, 1.015, 1.026, 1.041, 1.074, 1.125, 1.201, 1.302, 1.425, - 1.406, 1.294, 1.205, 1.126, 1.062, 1.031, 1.013, 1.009, 1.011, 1.019, 1.042, 1.079, 1.129, 1.203, 1.302, 1.435, - 1.415, 1.318, 1.229, 1.146, 1.076, 1.039, 1.019, 1.014, 1.017, 1.031, 1.053, 1.093, 1.144, 1.219, 1.314, 1.436, - 1.435, 1.348, 1.246, 1.164, 1.094, 1.059, 1.036, 1.032, 1.037, 1.049, 1.072, 1.114, 1.167, 1.257, 1.343, 1.462, - 1.471, 1.385, 1.278, 1.189, 1.124, 1.084, 1.064, 1.061, 1.069, 1.078, 1.101, 1.146, 1.207, 1.298, 1.415, 1.496, - 1.522, 1.436, 1.323, 1.228, 1.169, 1.118, 1.101, 1.094, 1.099, 1.113, 1.146, 1.194, 1.265, 1.353, 1.474, 1.571, - 1.578, 1.506, 1.378, 1.281, 1.211, 1.156, 1.135, 1.134, 1.139, 1.158, 1.194, 1.251, 1.327, 1.427, 1.559, 1.611 - ], - "sigma": 0.00121, - "sigma_Cb": 0.00115 - } - }, - { - "rpi.contrast": - { - "ce_enable": 1, - "gamma_curve": - [ - 0, 0, - 1024, 5040, - 2048, 9338, - 3072, 12356, - 4096, 15312, - 5120, 18051, - 6144, 20790, - 7168, 23193, - 8192, 25744, - 9216, 27942, - 10240, 30035, - 11264, 32005, - 12288, 33975, - 13312, 35815, - 14336, 37600, - 15360, 39168, - 16384, 40642, - 18432, 43379, - 20480, 45749, - 22528, 47753, - 24576, 49621, - 26624, 51253, - 28672, 52698, - 30720, 53796, - 32768, 54876, - 36864, 57012, - 40960, 58656, - 45056, 59954, - 49152, 61183, - 53248, 62355, - 57344, 63419, - 61440, 64476, - 65535, 65535 - ] - } - }, - { - "rpi.ccm": - { - "ccms": [ - { - "ct": 2360, - "ccm": - [ - 1.66078, -0.23588, -0.42491, - -0.47456, 1.82763, -0.35307, - -0.00545, -1.44729, 2.45273 - ] - }, - { - "ct": 2870, - "ccm": - [ - 1.78373, -0.55344, -0.23029, - -0.39951, 1.69701, -0.29751, - 0.01986, -1.06525, 2.04539 - ] - }, - { - "ct": 2970, - "ccm": - [ - 1.73511, -0.56973, -0.16537, - -0.36338, 1.69878, -0.33539, - -0.02354, -0.76813, 1.79168 - ] - }, - { - "ct": 3000, - "ccm": - [ - 2.06374, -0.92218, -0.14156, - -0.41721, 1.69289, -0.27568, - -0.00554, -0.92741, 1.93295 - ] - }, - { - "ct": 3700, - "ccm": - [ - 2.13792, -1.08136, -0.05655, - -0.34739, 1.58989, -0.24249, - -0.00349, -0.76789, 1.77138 - ] - }, - { - "ct": 3870, - "ccm": - [ - 1.83834, -0.70528, -0.13307, - -0.30499, 1.60523, -0.30024, - -0.05701, -0.58313, 1.64014 - ] - }, - { - "ct": 4000, - "ccm": - [ - 2.15741, -1.10295, -0.05447, - -0.34631, 1.61158, -0.26528, - -0.02723, -0.70288, 1.73011 - ] - }, - { - "ct": 4400, - "ccm": - [ - 2.05729, -0.95007, -0.10723, - -0.41712, 1.78606, -0.36894, - -0.11899, -0.55727, 1.67626 - ] - }, - { - "ct": 4715, - "ccm": - [ - 1.90255, -0.77478, -0.12777, - -0.31338, 1.88197, -0.56858, - -0.06001, -0.61785, 1.67786 - ] - }, - { - "ct": 5920, - "ccm": - [ - 1.98691, -0.84671, -0.14019, - -0.26581, 1.70615, -0.44035, - -0.09532, -0.47332, 1.56864 - ] - }, - { - "ct": 9050, - "ccm": - [ - 2.09255, -0.76541, -0.32714, - -0.28973, 2.27462, -0.98489, - -0.17299, -0.61275, 1.78574 - ] - } - ] - } - }, - { - "rpi.sharpen": { } - } - ] -} \ No newline at end of file diff --git a/src/ipa/raspberrypi/data/imx477_noir.json b/src/ipa/raspberrypi/data/imx477_noir.json deleted file mode 100644 index 52d7f072..00000000 --- a/src/ipa/raspberrypi/data/imx477_noir.json +++ /dev/null @@ -1,429 +0,0 @@ -{ - "version": 2.0, - "target": "bcm2835", - "algorithms": [ - { - "rpi.black_level": - { - "black_level": 4096 - } - }, - { - "rpi.dpc": { } - }, - { - "rpi.lux": - { - "reference_shutter_speed": 27242, - "reference_gain": 1.0, - "reference_aperture": 1.0, - "reference_lux": 830, - "reference_Y": 17755 - } - }, - { - "rpi.noise": - { - "reference_constant": 0, - "reference_slope": 2.767 - } - }, - { - "rpi.geq": - { - "offset": 204, - "slope": 0.01078 - } - }, - { - "rpi.sdn": { } - }, - { - "rpi.awb": - { - "bayes": 0 - } - }, - { - "rpi.agc": - { - "metering_modes": - { - "centre-weighted": - { - "weights": [ 3, 3, 3, 2, 2, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0 ] - }, - "spot": - { - "weights": [ 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ] - }, - "matrix": - { - "weights": [ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ] - } - }, - "exposure_modes": - { - "normal": - { - "shutter": [ 100, 10000, 30000, 60000, 66666 ], - "gain": [ 1.0, 2.0, 4.0, 6.0, 8.0 ] - }, - "short": - { - "shutter": [ 100, 5000, 10000, 20000, 33333 ], - "gain": [ 1.0, 2.0, 4.0, 6.0, 8.0 ] - }, - "long": - { - "shutter": [ 100, 10000, 30000, 60000, 120000 ], - "gain": [ 1.0, 2.0, 4.0, 6.0, 12.0 ] - } - }, - "constraint_modes": - { - "normal": [ - { - "bound": "LOWER", - "q_lo": 0.98, - "q_hi": 1.0, - "y_target": - [ - 0, 0.3, - 1000, 0.3 - ] - } - ], - "highlight": [ - { - "bound": "LOWER", - "q_lo": 0.98, - "q_hi": 1.0, - "y_target": - [ - 0, 0.3, - 1000, 0.3 - ] - }, - { - "bound": "UPPER", - "q_lo": 0.98, - "q_hi": 1.0, - "y_target": - [ - 0, 0.8, - 1000, 0.8 - ] - } - ], - "shadows": [ - { - "bound": "LOWER", - "q_lo": 0.0, - "q_hi": 0.5, - "y_target": - [ - 0, 0.17, - 1000, 0.17 - ] - } - ] - }, - "y_target": - [ - 0, 0.16, - 1000, 0.165, - 10000, 0.17 - ] - } - }, - { - "rpi.alsc": - { - "omega": 1.3, - "n_iter": 100, - "luminance_strength": 0.5, - "calibrations_Cr": [ - { - "ct": 2960, - "table": - [ - 2.088, 2.086, 2.082, 2.081, 2.077, 2.071, 2.068, 2.068, 2.072, 2.073, 2.075, 2.078, 2.084, 2.092, 2.095, 2.098, - 2.086, 2.084, 2.079, 2.078, 2.075, 2.068, 2.064, 2.063, 2.068, 2.071, 2.072, 2.075, 2.081, 2.089, 2.092, 2.094, - 2.083, 2.081, 2.077, 2.072, 2.069, 2.062, 2.059, 2.059, 2.063, 2.067, 2.069, 2.072, 2.079, 2.088, 2.089, 2.089, - 2.081, 2.077, 2.072, 2.068, 2.065, 2.058, 2.055, 2.054, 2.057, 2.062, 2.066, 2.069, 2.077, 2.084, 2.086, 2.086, - 2.078, 2.075, 2.069, 2.065, 2.061, 2.055, 2.052, 2.049, 2.051, 2.056, 2.062, 2.065, 2.072, 2.079, 2.081, 2.079, - 2.079, 2.075, 2.069, 2.064, 2.061, 2.053, 2.049, 2.046, 2.049, 2.051, 2.057, 2.062, 2.069, 2.075, 2.077, 2.075, - 2.082, 2.079, 2.072, 2.065, 2.061, 2.054, 2.049, 2.047, 2.049, 2.051, 2.056, 2.061, 2.066, 2.073, 2.073, 2.069, - 2.086, 2.082, 2.075, 2.068, 2.062, 2.054, 2.051, 2.049, 2.051, 2.052, 2.056, 2.061, 2.066, 2.073, 2.073, 2.072, - 2.088, 2.086, 2.079, 2.074, 2.066, 2.057, 2.051, 2.051, 2.054, 2.055, 2.056, 2.061, 2.067, 2.072, 2.073, 2.072, - 2.091, 2.087, 2.079, 2.075, 2.068, 2.057, 2.052, 2.052, 2.056, 2.055, 2.055, 2.059, 2.066, 2.072, 2.072, 2.072, - 2.093, 2.088, 2.081, 2.077, 2.069, 2.059, 2.054, 2.054, 2.057, 2.056, 2.056, 2.058, 2.066, 2.072, 2.073, 2.073, - 2.095, 2.091, 2.084, 2.078, 2.075, 2.067, 2.057, 2.057, 2.059, 2.059, 2.058, 2.059, 2.068, 2.073, 2.075, 2.078 - ] - }, - { - "ct": 4850, - "table": - [ - 2.973, 2.968, 2.956, 2.943, 2.941, 2.932, 2.923, 2.921, 2.924, 2.929, 2.931, 2.939, 2.953, 2.965, 2.966, 2.976, - 2.969, 2.962, 2.951, 2.941, 2.934, 2.928, 2.919, 2.918, 2.919, 2.923, 2.927, 2.933, 2.945, 2.957, 2.962, 2.962, - 2.964, 2.956, 2.944, 2.932, 2.929, 2.924, 2.915, 2.914, 2.915, 2.919, 2.924, 2.928, 2.941, 2.952, 2.958, 2.959, - 2.957, 2.951, 2.939, 2.928, 2.924, 2.919, 2.913, 2.911, 2.911, 2.915, 2.919, 2.925, 2.936, 2.947, 2.952, 2.953, - 2.954, 2.947, 2.935, 2.924, 2.919, 2.915, 2.908, 2.906, 2.906, 2.907, 2.914, 2.921, 2.932, 2.941, 2.943, 2.942, - 2.953, 2.946, 2.932, 2.921, 2.916, 2.911, 2.904, 2.902, 2.901, 2.904, 2.909, 2.919, 2.926, 2.937, 2.939, 2.939, - 2.953, 2.947, 2.932, 2.918, 2.915, 2.909, 2.903, 2.901, 2.901, 2.906, 2.911, 2.918, 2.924, 2.936, 2.936, 2.932, - 2.956, 2.948, 2.934, 2.919, 2.916, 2.908, 2.903, 2.901, 2.902, 2.907, 2.909, 2.917, 2.926, 2.936, 2.939, 2.939, - 2.957, 2.951, 2.936, 2.923, 2.917, 2.907, 2.904, 2.901, 2.902, 2.908, 2.911, 2.919, 2.929, 2.939, 2.942, 2.942, - 2.961, 2.951, 2.936, 2.922, 2.918, 2.906, 2.904, 2.901, 2.901, 2.907, 2.911, 2.921, 2.931, 2.941, 2.942, 2.944, - 2.964, 2.954, 2.936, 2.924, 2.918, 2.909, 2.905, 2.905, 2.905, 2.907, 2.912, 2.923, 2.933, 2.942, 2.944, 2.944, - 2.964, 2.958, 2.943, 2.927, 2.921, 2.914, 2.909, 2.907, 2.907, 2.912, 2.916, 2.928, 2.936, 2.944, 2.947, 2.952 - ] - }, - { - "ct": 5930, - "table": - [ - 3.312, 3.308, 3.301, 3.294, 3.288, 3.277, 3.268, 3.261, 3.259, 3.261, 3.267, 3.273, 3.285, 3.301, 3.303, 3.312, - 3.308, 3.304, 3.294, 3.291, 3.283, 3.271, 3.263, 3.259, 3.257, 3.258, 3.261, 3.268, 3.278, 3.293, 3.299, 3.299, - 3.302, 3.296, 3.288, 3.282, 3.276, 3.267, 3.259, 3.254, 3.252, 3.253, 3.256, 3.261, 3.273, 3.289, 3.292, 3.292, - 3.296, 3.289, 3.282, 3.276, 3.269, 3.263, 3.256, 3.251, 3.248, 3.249, 3.251, 3.257, 3.268, 3.279, 3.284, 3.284, - 3.292, 3.285, 3.279, 3.271, 3.264, 3.257, 3.249, 3.243, 3.241, 3.241, 3.246, 3.252, 3.261, 3.274, 3.275, 3.273, - 3.291, 3.285, 3.276, 3.268, 3.259, 3.251, 3.242, 3.239, 3.236, 3.238, 3.244, 3.248, 3.258, 3.268, 3.269, 3.265, - 3.294, 3.288, 3.275, 3.266, 3.257, 3.248, 3.239, 3.238, 3.237, 3.238, 3.243, 3.246, 3.255, 3.264, 3.264, 3.257, - 3.297, 3.293, 3.279, 3.268, 3.258, 3.249, 3.238, 3.237, 3.239, 3.239, 3.243, 3.245, 3.255, 3.264, 3.264, 3.263, - 3.301, 3.295, 3.281, 3.271, 3.259, 3.248, 3.237, 3.237, 3.239, 3.241, 3.243, 3.246, 3.257, 3.265, 3.266, 3.264, - 3.306, 3.295, 3.279, 3.271, 3.261, 3.247, 3.235, 3.234, 3.239, 3.239, 3.243, 3.247, 3.258, 3.265, 3.265, 3.264, - 3.308, 3.297, 3.279, 3.272, 3.261, 3.249, 3.239, 3.239, 3.241, 3.243, 3.245, 3.248, 3.261, 3.265, 3.266, 3.265, - 3.309, 3.301, 3.286, 3.276, 3.267, 3.256, 3.246, 3.242, 3.244, 3.244, 3.249, 3.253, 3.263, 3.267, 3.271, 3.274 - ] - } - ], - "calibrations_Cb": [ - { - "ct": 2960, - "table": - [ - 2.133, 2.134, 2.139, 2.143, 2.148, 2.155, 2.158, 2.158, 2.158, 2.161, 2.161, 2.162, 2.159, 2.156, 2.152, 2.151, - 2.132, 2.133, 2.135, 2.142, 2.147, 2.153, 2.158, 2.158, 2.158, 2.158, 2.159, 2.159, 2.157, 2.154, 2.151, 2.148, - 2.133, 2.133, 2.135, 2.142, 2.149, 2.154, 2.158, 2.158, 2.157, 2.156, 2.158, 2.157, 2.155, 2.153, 2.148, 2.146, - 2.133, 2.133, 2.138, 2.145, 2.149, 2.154, 2.158, 2.159, 2.158, 2.155, 2.157, 2.156, 2.153, 2.149, 2.146, 2.144, - 2.133, 2.134, 2.139, 2.146, 2.149, 2.154, 2.158, 2.159, 2.159, 2.156, 2.154, 2.154, 2.149, 2.145, 2.143, 2.139, - 2.135, 2.135, 2.139, 2.146, 2.151, 2.155, 2.158, 2.159, 2.158, 2.156, 2.153, 2.151, 2.146, 2.143, 2.139, 2.136, - 2.135, 2.135, 2.138, 2.145, 2.151, 2.154, 2.157, 2.158, 2.157, 2.156, 2.153, 2.151, 2.147, 2.143, 2.141, 2.137, - 2.135, 2.134, 2.135, 2.141, 2.149, 2.154, 2.157, 2.157, 2.157, 2.157, 2.157, 2.153, 2.149, 2.146, 2.142, 2.139, - 2.132, 2.133, 2.135, 2.139, 2.148, 2.153, 2.158, 2.159, 2.159, 2.161, 2.161, 2.157, 2.154, 2.149, 2.144, 2.141, - 2.132, 2.133, 2.135, 2.141, 2.149, 2.155, 2.161, 2.161, 2.162, 2.162, 2.163, 2.159, 2.154, 2.149, 2.144, 2.138, - 2.136, 2.136, 2.137, 2.143, 2.149, 2.156, 2.162, 2.163, 2.162, 2.163, 2.164, 2.161, 2.157, 2.152, 2.146, 2.138, - 2.137, 2.137, 2.141, 2.147, 2.152, 2.157, 2.162, 2.162, 2.159, 2.161, 2.162, 2.162, 2.157, 2.152, 2.148, 2.148 - ] - }, - { - "ct": 4850, - "table": - [ - 1.463, 1.464, 1.471, 1.478, 1.479, 1.483, 1.484, 1.486, 1.486, 1.484, 1.483, 1.481, 1.478, 1.475, 1.471, 1.468, - 1.463, 1.463, 1.468, 1.476, 1.479, 1.482, 1.484, 1.487, 1.486, 1.484, 1.483, 1.482, 1.478, 1.473, 1.469, 1.468, - 1.463, 1.464, 1.468, 1.476, 1.479, 1.483, 1.484, 1.486, 1.486, 1.485, 1.484, 1.482, 1.477, 1.473, 1.469, 1.468, - 1.463, 1.464, 1.469, 1.477, 1.481, 1.483, 1.485, 1.487, 1.487, 1.485, 1.485, 1.482, 1.478, 1.474, 1.469, 1.468, - 1.465, 1.465, 1.471, 1.478, 1.481, 1.484, 1.486, 1.488, 1.488, 1.487, 1.485, 1.482, 1.477, 1.472, 1.468, 1.467, - 1.465, 1.466, 1.472, 1.479, 1.482, 1.485, 1.486, 1.488, 1.488, 1.486, 1.484, 1.479, 1.475, 1.472, 1.468, 1.466, - 1.466, 1.466, 1.472, 1.478, 1.482, 1.484, 1.485, 1.488, 1.487, 1.485, 1.483, 1.479, 1.475, 1.472, 1.469, 1.468, - 1.465, 1.466, 1.469, 1.476, 1.481, 1.485, 1.485, 1.486, 1.486, 1.485, 1.483, 1.479, 1.477, 1.474, 1.471, 1.469, - 1.464, 1.465, 1.469, 1.476, 1.481, 1.484, 1.485, 1.487, 1.487, 1.486, 1.485, 1.481, 1.478, 1.475, 1.471, 1.469, - 1.463, 1.464, 1.469, 1.477, 1.481, 1.485, 1.485, 1.488, 1.488, 1.487, 1.486, 1.481, 1.478, 1.475, 1.471, 1.468, - 1.464, 1.465, 1.471, 1.478, 1.482, 1.486, 1.486, 1.488, 1.488, 1.487, 1.486, 1.481, 1.478, 1.475, 1.472, 1.468, - 1.465, 1.466, 1.472, 1.481, 1.483, 1.487, 1.487, 1.488, 1.488, 1.486, 1.485, 1.481, 1.479, 1.476, 1.473, 1.472 - ] - }, - { - "ct": 5930, - "table": - [ - 1.443, 1.444, 1.448, 1.453, 1.459, 1.463, 1.465, 1.467, 1.469, 1.469, 1.467, 1.466, 1.462, 1.457, 1.454, 1.451, - 1.443, 1.444, 1.445, 1.451, 1.459, 1.463, 1.465, 1.467, 1.469, 1.469, 1.467, 1.465, 1.461, 1.456, 1.452, 1.451, - 1.444, 1.444, 1.445, 1.451, 1.459, 1.463, 1.466, 1.468, 1.469, 1.469, 1.467, 1.465, 1.461, 1.456, 1.452, 1.449, - 1.444, 1.444, 1.447, 1.452, 1.459, 1.464, 1.467, 1.469, 1.471, 1.469, 1.467, 1.466, 1.461, 1.456, 1.452, 1.449, - 1.444, 1.445, 1.448, 1.452, 1.459, 1.465, 1.469, 1.471, 1.471, 1.471, 1.468, 1.465, 1.461, 1.455, 1.451, 1.449, - 1.445, 1.446, 1.449, 1.453, 1.461, 1.466, 1.469, 1.471, 1.472, 1.469, 1.467, 1.465, 1.459, 1.455, 1.451, 1.447, - 1.446, 1.446, 1.449, 1.453, 1.461, 1.466, 1.469, 1.469, 1.469, 1.469, 1.467, 1.465, 1.459, 1.455, 1.452, 1.449, - 1.446, 1.446, 1.447, 1.451, 1.459, 1.466, 1.469, 1.469, 1.469, 1.469, 1.467, 1.465, 1.461, 1.457, 1.454, 1.451, - 1.444, 1.444, 1.447, 1.451, 1.459, 1.466, 1.469, 1.469, 1.471, 1.471, 1.468, 1.466, 1.462, 1.458, 1.454, 1.452, - 1.444, 1.444, 1.448, 1.453, 1.459, 1.466, 1.469, 1.471, 1.472, 1.472, 1.468, 1.466, 1.462, 1.458, 1.454, 1.449, - 1.446, 1.447, 1.449, 1.454, 1.461, 1.466, 1.471, 1.471, 1.471, 1.471, 1.468, 1.466, 1.462, 1.459, 1.455, 1.449, - 1.447, 1.447, 1.452, 1.457, 1.462, 1.468, 1.472, 1.472, 1.471, 1.471, 1.468, 1.466, 1.462, 1.459, 1.456, 1.455 - ] - } - ], - "luminance_lut": - [ - 1.548, 1.499, 1.387, 1.289, 1.223, 1.183, 1.164, 1.154, 1.153, 1.169, 1.211, 1.265, 1.345, 1.448, 1.581, 1.619, - 1.513, 1.412, 1.307, 1.228, 1.169, 1.129, 1.105, 1.098, 1.103, 1.127, 1.157, 1.209, 1.272, 1.361, 1.481, 1.583, - 1.449, 1.365, 1.257, 1.175, 1.124, 1.085, 1.062, 1.054, 1.059, 1.079, 1.113, 1.151, 1.211, 1.293, 1.407, 1.488, - 1.424, 1.324, 1.222, 1.139, 1.089, 1.056, 1.034, 1.031, 1.034, 1.049, 1.075, 1.115, 1.164, 1.241, 1.351, 1.446, - 1.412, 1.297, 1.203, 1.119, 1.069, 1.039, 1.021, 1.016, 1.022, 1.032, 1.052, 1.086, 1.135, 1.212, 1.321, 1.439, - 1.406, 1.287, 1.195, 1.115, 1.059, 1.028, 1.014, 1.012, 1.015, 1.026, 1.041, 1.074, 1.125, 1.201, 1.302, 1.425, - 1.406, 1.294, 1.205, 1.126, 1.062, 1.031, 1.013, 1.009, 1.011, 1.019, 1.042, 1.079, 1.129, 1.203, 1.302, 1.435, - 1.415, 1.318, 1.229, 1.146, 1.076, 1.039, 1.019, 1.014, 1.017, 1.031, 1.053, 1.093, 1.144, 1.219, 1.314, 1.436, - 1.435, 1.348, 1.246, 1.164, 1.094, 1.059, 1.036, 1.032, 1.037, 1.049, 1.072, 1.114, 1.167, 1.257, 1.343, 1.462, - 1.471, 1.385, 1.278, 1.189, 1.124, 1.084, 1.064, 1.061, 1.069, 1.078, 1.101, 1.146, 1.207, 1.298, 1.415, 1.496, - 1.522, 1.436, 1.323, 1.228, 1.169, 1.118, 1.101, 1.094, 1.099, 1.113, 1.146, 1.194, 1.265, 1.353, 1.474, 1.571, - 1.578, 1.506, 1.378, 1.281, 1.211, 1.156, 1.135, 1.134, 1.139, 1.158, 1.194, 1.251, 1.327, 1.427, 1.559, 1.611 - ], - "sigma": 0.00121, - "sigma_Cb": 0.00115 - } - }, - { - "rpi.contrast": - { - "ce_enable": 1, - "gamma_curve": - [ - 0, 0, - 1024, 5040, - 2048, 9338, - 3072, 12356, - 4096, 15312, - 5120, 18051, - 6144, 20790, - 7168, 23193, - 8192, 25744, - 9216, 27942, - 10240, 30035, - 11264, 32005, - 12288, 33975, - 13312, 35815, - 14336, 37600, - 15360, 39168, - 16384, 40642, - 18432, 43379, - 20480, 45749, - 22528, 47753, - 24576, 49621, - 26624, 51253, - 28672, 52698, - 30720, 53796, - 32768, 54876, - 36864, 57012, - 40960, 58656, - 45056, 59954, - 49152, 61183, - 53248, 62355, - 57344, 63419, - 61440, 64476, - 65535, 65535 - ] - } - }, - { - "rpi.ccm": - { - "ccms": [ - { - "ct": 2360, - "ccm": - [ - 1.66078, -0.23588, -0.42491, - -0.47456, 1.82763, -0.35307, - -0.00545, -1.44729, 2.45273 - ] - }, - { - "ct": 2870, - "ccm": - [ - 1.78373, -0.55344, -0.23029, - -0.39951, 1.69701, -0.29751, - 0.01986, -1.06525, 2.04539 - ] - }, - { - "ct": 2970, - "ccm": - [ - 1.73511, -0.56973, -0.16537, - -0.36338, 1.69878, -0.33539, - -0.02354, -0.76813, 1.79168 - ] - }, - { - "ct": 3000, - "ccm": - [ - 2.06374, -0.92218, -0.14156, - -0.41721, 1.69289, -0.27568, - -0.00554, -0.92741, 1.93295 - ] - }, - { - "ct": 3700, - "ccm": - [ - 2.13792, -1.08136, -0.05655, - -0.34739, 1.58989, -0.24249, - -0.00349, -0.76789, 1.77138 - ] - }, - { - "ct": 3870, - "ccm": - [ - 1.83834, -0.70528, -0.13307, - -0.30499, 1.60523, -0.30024, - -0.05701, -0.58313, 1.64014 - ] - }, - { - "ct": 4000, - "ccm": - [ - 2.15741, -1.10295, -0.05447, - -0.34631, 1.61158, -0.26528, - -0.02723, -0.70288, 1.73011 - ] - }, - { - "ct": 4400, - "ccm": - [ - 2.05729, -0.95007, -0.10723, - -0.41712, 1.78606, -0.36894, - -0.11899, -0.55727, 1.67626 - ] - }, - { - "ct": 4715, - "ccm": - [ - 1.90255, -0.77478, -0.12777, - -0.31338, 1.88197, -0.56858, - -0.06001, -0.61785, 1.67786 - ] - }, - { - "ct": 5920, - "ccm": - [ - 1.98691, -0.84671, -0.14019, - -0.26581, 1.70615, -0.44035, - -0.09532, -0.47332, 1.56864 - ] - }, - { - "ct": 9050, - "ccm": - [ - 2.09255, -0.76541, -0.32714, - -0.28973, 2.27462, -0.98489, - -0.17299, -0.61275, 1.78574 - ] - } - ] - } - }, - { - "rpi.sharpen": { } - } - ] -} \ No newline at end of file diff --git a/src/ipa/raspberrypi/data/imx477_scientific.json b/src/ipa/raspberrypi/data/imx477_scientific.json deleted file mode 100644 index 26c692fd..00000000 --- a/src/ipa/raspberrypi/data/imx477_scientific.json +++ /dev/null @@ -1,479 +0,0 @@ -{ - "version": 2.0, - "target": "bcm2835", - "algorithms": [ - { - "rpi.black_level": - { - "black_level": 4096 - } - }, - { - "rpi.dpc": { } - }, - { - "rpi.lux": - { - "reference_shutter_speed": 27242, - "reference_gain": 1.0, - "reference_aperture": 1.0, - "reference_lux": 830, - "reference_Y": 17755 - } - }, - { - "rpi.noise": - { - "reference_constant": 0, - "reference_slope": 2.767 - } - }, - { - "rpi.geq": - { - "offset": 204, - "slope": 0.01078 - } - }, - { - "rpi.sdn": { } - }, - { - "rpi.awb": - { - "priors": [ - { - "lux": 0, - "prior": - [ - 2000, 1.0, - 3000, 0.0, - 13000, 0.0 - ] - }, - { - "lux": 800, - "prior": - [ - 2000, 0.0, - 6000, 2.0, - 13000, 2.0 - ] - }, - { - "lux": 1500, - "prior": - [ - 2000, 0.0, - 4000, 1.0, - 6000, 6.0, - 6500, 7.0, - 7000, 1.0, - 13000, 1.0 - ] - } - ], - "modes": - { - "auto": - { - "lo": 2500, - "hi": 8000 - }, - "incandescent": - { - "lo": 2500, - "hi": 3000 - }, - "tungsten": - { - "lo": 3000, - "hi": 3500 - }, - "fluorescent": - { - "lo": 4000, - "hi": 4700 - }, - "indoor": - { - "lo": 3000, - "hi": 5000 - }, - "daylight": - { - "lo": 5500, - "hi": 6500 - }, - "cloudy": - { - "lo": 7000, - "hi": 8600 - } - }, - "bayes": 1, - "ct_curve": - [ - 2000.0, 0.6331025775790707, 0.27424225990946915, - 2200.0, 0.5696117366212947, 0.3116091368689487, - 2400.0, 0.5204264653110015, 0.34892179554105873, - 2600.0, 0.48148675531667223, 0.38565229719076793, - 2800.0, 0.450085403501908, 0.42145684622485047, - 3000.0, 0.42436130159169017, 0.45611835670028816, - 3200.0, 0.40300023695527337, 0.48950766215198593, - 3400.0, 0.3850520052612984, 0.5215567075837261, - 3600.0, 0.36981508088230314, 0.5522397906415475, - 4100.0, 0.333468007836758, 0.5909770465167908, - 4600.0, 0.31196097364221376, 0.6515706327327178, - 5100.0, 0.2961860409294588, 0.7068178946570284, - 5600.0, 0.2842607232745885, 0.7564837749584288, - 6100.0, 0.2750265787051251, 0.8006183524920533, - 6600.0, 0.2677057225584924, 0.8398879225373039, - 7100.0, 0.2617955199757274, 0.8746456080032436, - 7600.0, 0.25693714288250125, 0.905569559506562, - 8100.0, 0.25287531441063316, 0.9331696750390895, - 8600.0, 0.24946601483331993, 0.9576820904825795 - ], - "sensitivity_r": 1.05, - "sensitivity_b": 1.05, - "transverse_pos": 0.0238, - "transverse_neg": 0.04429, - "coarse_step": 0.1 - } - }, - { - "rpi.agc": - { - "metering_modes": - { - "centre-weighted": - { - "weights": [ 3, 3, 3, 2, 2, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0 ] - }, - "spot": - { - "weights": [ 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ] - }, - "matrix": - { - "weights": [ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ] - } - }, - "exposure_modes": - { - "normal": - { - "shutter": [ 100, 10000, 30000, 60000, 66666 ], - "gain": [ 1.0, 2.0, 4.0, 6.0, 8.0 ] - }, - "short": - { - "shutter": [ 100, 5000, 10000, 20000, 33333 ], - "gain": [ 1.0, 2.0, 4.0, 6.0, 8.0 ] - }, - "long": - { - "shutter": [ 100, 10000, 30000, 60000, 120000 ], - "gain": [ 1.0, 2.0, 4.0, 6.0, 12.0 ] - } - }, - "constraint_modes": - { - "normal": [ - { - "bound": "LOWER", - "q_lo": 0.98, - "q_hi": 1.0, - "y_target": - [ - 0, 0.3, - 1000, 0.3 - ] - } - ], - "highlight": [ - { - "bound": "LOWER", - "q_lo": 0.98, - "q_hi": 1.0, - "y_target": - [ - 0, 0.3, - 1000, 0.3 - ] - }, - { - "bound": "UPPER", - "q_lo": 0.98, - "q_hi": 1.0, - "y_target": - [ - 0, 0.8, - 1000, 0.8 - ] - } - ], - "shadows": [ - { - "bound": "LOWER", - "q_lo": 0.0, - "q_hi": 0.5, - "y_target": - [ - 0, 0.17, - 1000, 0.17 - ] - } - ] - }, - "y_target": - [ - 0, 0.16, - 1000, 0.165, - 10000, 0.17 - ] - } - }, - { - "rpi.contrast": - { - "ce_enable": 0, - "gamma_curve": - [ - 0, 0, - 512, 2304, - 1024, 4608, - 1536, 6573, - 2048, 8401, - 2560, 9992, - 3072, 11418, - 3584, 12719, - 4096, 13922, - 4608, 15045, - 5120, 16103, - 5632, 17104, - 6144, 18056, - 6656, 18967, - 7168, 19839, - 7680, 20679, - 8192, 21488, - 9216, 23028, - 10240, 24477, - 11264, 25849, - 12288, 27154, - 13312, 28401, - 14336, 29597, - 15360, 30747, - 16384, 31856, - 17408, 32928, - 18432, 33966, - 19456, 34973, - 20480, 35952, - 22528, 37832, - 24576, 39621, - 26624, 41330, - 28672, 42969, - 30720, 44545, - 32768, 46065, - 34816, 47534, - 36864, 48956, - 38912, 50336, - 40960, 51677, - 43008, 52982, - 45056, 54253, - 47104, 55493, - 49152, 56704, - 51200, 57888, - 53248, 59046, - 55296, 60181, - 57344, 61292, - 59392, 62382, - 61440, 63452, - 63488, 64503, - 65535, 65535 - ] - } - }, - { - "rpi.ccm": - { - "ccms": [ - { - "ct": 2000, - "ccm": - [ - 1.5813882365848004, -0.35293683714581114, -0.27378771561617715, - -0.4347297185453639, 1.5792631087746074, -0.12102601986382337, - 0.2322290578987574, -1.4382672640468128, 2.1386425781770755 - ] - }, - { - "ct": 2200, - "ccm": - [ - 1.6322048484088305, -0.45932286857238486, -0.21373542690252198, - -0.3970719209901105, 1.5877868651467202, -0.17249380832122455, - 0.20753774825903412, -1.2660673594740142, 2.005654261091916 - ] - }, - { - "ct": 2400, - "ccm": - [ - 1.6766610071470398, -0.5447101051688111, -0.16838641107407676, - -0.3659845183388154, 1.592223692670396, -0.2127091997471162, - 0.1833964516767549, -1.1339155942419321, 1.9089342978542396 - ] - }, - { - "ct": 2600, - "ccm": - [ - 1.7161984340622154, -0.6152585785678794, -0.1331100845092582, - -0.33972082628066275, 1.5944888273736966, -0.2453979465898787, - 0.1615577497676328, -1.0298684958833109, 1.8357854177422053 - ] - }, - { - "ct": 2800, - "ccm": - [ - 1.7519307259815728, -0.6748682080165339, -0.10515169074540848, - -0.3171703484479931, 1.5955820297498486, -0.2727395854813966, - 0.14230870739974305, -0.9460976023551511, 1.778709391659538 - ] - }, - { - "ct": 3000, - "ccm": - [ - 1.7846716625128374, -0.7261240476375332, -0.08274697420358428, - -0.2975654035173307, 1.5960425637021738, -0.2961043416505157, - 0.12546426281675097, -0.8773434727076518, 1.7330356805246685 - ] - }, - { - "ct": 3200, - "ccm": - [ - 1.8150085872943436, -0.7708109672515514, -0.06469468211419174, - -0.2803468940646277, 1.596168842967451, -0.3164044170681625, - 0.11071494533513807, -0.8199772290209191, 1.69572135046367 - ] - }, - { - "ct": 3400, - "ccm": - [ - 1.8433668304932087, -0.8102060605062592, -0.05013485852801454, - -0.2650934036324084, 1.5961288492969294, -0.33427554893845535, - 0.0977478941863518, -0.7714303112098978, 1.6647070820146963 - ] - }, - { - "ct": 3600, - "ccm": - [ - 1.8700575831917468, -0.8452518300291346, -0.03842644337477299, - -0.2514794528347016, 1.5960178299141876, -0.3501774949366156, - 0.08628520830733245, -0.729841503339915, 1.638553343939267 - ] - }, - { - "ct": 4100, - "ccm": - [ - 1.8988700903560716, -0.8911278803351247, -0.018848644425650693, - -0.21487101487384094, 1.599236541382614, -0.39405450457918206, - 0.08251488056482173, -0.7178919368326191, 1.6267009056502704 - ] - }, - { - "ct": 4600, - "ccm": - [ - 1.960355191764125, -0.9624344812121991, -0.0017122408632169205, - -0.19444620905212898, 1.5978493736948447, -0.416727638296156, - 0.06310261513271084, -0.6483790952487849, 1.5834605477213093 - ] - }, - { - "ct": 5100, - "ccm": - [ - 2.014680536961399, -1.0195930302148566, 0.007728256612638915, - -0.17751999660735496, 1.5977081555831, -0.4366085498741474, - 0.04741267583041334, -0.5950327902073489, 1.5512919847321853 - ] - }, - { - "ct": 5600, - "ccm": - [ - 2.062652337917251, -1.0658386679125478, 0.011886354256281267, - -0.16319197721451495, 1.598363237584736, -0.45422061523742235, - 0.03465810928795378, -0.5535454108047286, 1.5269025836946852 - ] - }, - { - "ct": 6100, - "ccm": - [ - 2.104985902038069, -1.103597868736314, 0.012503517136539277, - -0.15090797064906178, 1.5994703078166095, -0.4698414300864995, - 0.02421766063474242, -0.5208922818196823, 1.5081270847783788 - ] - }, - { - "ct": 6600, - "ccm": - [ - 2.1424988751299714, -1.134760232367728, 0.010730356010435522, - -0.14021846798466234, 1.600822462230719, -0.48379204794526487, - 0.015521315410496622, -0.49463630325832275, 1.4933313534840327 - ] - }, - { - "ct": 7100, - "ccm": - [ - 2.1758034100130925, -1.1607558481037359, 0.007452724895469076, - -0.13085694672641826, 1.6022648614493245, -0.4962330524084075, - 0.008226943206113427, -0.4733077192319791, 1.4815336120437468 - ] - }, - { - "ct": 7600, - "ccm": - [ - 2.205529206931895, -1.1826662383072108, 0.0032019529917605167, - -0.122572009780486, 1.6037258133595753, -0.5073973734282445, - 0.0020132587619863425, -0.4556590236414181, 1.471939788496745 - ] - }, - { - "ct": 8100, - "ccm": - [ - 2.232224969223067, -1.2013672897252885, -0.0016234598095482985, - -0.11518026734442414, 1.6051544769439803, -0.5174558699422255, - -0.0033378143542219835, -0.4408590373867774, 1.4640252230667452 - ] - }, - { - "ct": 8600, - "ccm": - [ - 2.256082295891265, -1.2173210549996634, -0.0067231350481711675, - -0.10860272839843167, 1.6065150139140594, -0.5264728573611493, - -0.007952618707984149, -0.4284003574050791, 1.4574646927117558 - ] - } - ] - } - }, - { - "rpi.sharpen": { } - } - ] -} \ No newline at end of file diff --git a/src/ipa/raspberrypi/data/imx477_v1.json b/src/ipa/raspberrypi/data/imx477_v1.json deleted file mode 100644 index d6402009..00000000 --- a/src/ipa/raspberrypi/data/imx477_v1.json +++ /dev/null @@ -1,516 +0,0 @@ -{ - "version": 2.0, - "target": "bcm2835", - "algorithms": [ - { - "rpi.black_level": - { - "black_level": 4096 - } - }, - { - "rpi.dpc": { } - }, - { - "rpi.lux": - { - "reference_shutter_speed": 27242, - "reference_gain": 1.0, - "reference_aperture": 1.0, - "reference_lux": 830, - "reference_Y": 17755 - } - }, - { - "rpi.noise": - { - "reference_constant": 0, - "reference_slope": 2.767 - } - }, - { - "rpi.geq": - { - "offset": 204, - "slope": 0.01078 - } - }, - { - "rpi.sdn": { } - }, - { - "rpi.awb": - { - "priors": [ - { - "lux": 0, - "prior": - [ - 2000, 1.0, - 3000, 0.0, - 13000, 0.0 - ] - }, - { - "lux": 800, - "prior": - [ - 2000, 0.0, - 6000, 2.0, - 13000, 2.0 - ] - }, - { - "lux": 1500, - "prior": - [ - 2000, 0.0, - 4000, 1.0, - 6000, 6.0, - 6500, 7.0, - 7000, 1.0, - 13000, 1.0 - ] - } - ], - "modes": - { - "auto": - { - "lo": 2500, - "hi": 8000 - }, - "incandescent": - { - "lo": 2500, - "hi": 3000 - }, - "tungsten": - { - "lo": 3000, - "hi": 3500 - }, - "fluorescent": - { - "lo": 4000, - "hi": 4700 - }, - "indoor": - { - "lo": 3000, - "hi": 5000 - }, - "daylight": - { - "lo": 5500, - "hi": 6500 - }, - "cloudy": - { - "lo": 7000, - "hi": 8600 - } - }, - "bayes": 1, - "ct_curve": - [ - 2360.0, 0.6009, 0.3093, - 2870.0, 0.5047, 0.3936, - 2970.0, 0.4782, 0.4221, - 3700.0, 0.4212, 0.4923, - 3870.0, 0.4037, 0.5166, - 4000.0, 0.3965, 0.5271, - 4400.0, 0.3703, 0.5666, - 4715.0, 0.3411, 0.6147, - 5920.0, 0.3108, 0.6687, - 9050.0, 0.2524, 0.7856 - ], - "sensitivity_r": 1.05, - "sensitivity_b": 1.05, - "transverse_pos": 0.0238, - "transverse_neg": 0.04429 - } - }, - { - "rpi.agc": - { - "metering_modes": - { - "centre-weighted": - { - "weights": [ 3, 3, 3, 2, 2, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0 ] - }, - "spot": - { - "weights": [ 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ] - }, - "matrix": - { - "weights": [ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ] - } - }, - "exposure_modes": - { - "normal": - { - "shutter": [ 100, 10000, 30000, 60000, 66666 ], - "gain": [ 1.0, 2.0, 4.0, 6.0, 8.0 ] - }, - "short": - { - "shutter": [ 100, 5000, 10000, 20000, 33333 ], - "gain": [ 1.0, 2.0, 4.0, 6.0, 8.0 ] - }, - "long": - { - "shutter": [ 100, 10000, 30000, 60000, 120000 ], - "gain": [ 1.0, 2.0, 4.0, 6.0, 12.0 ] - } - }, - "constraint_modes": - { - "normal": [ - { - "bound": "LOWER", - "q_lo": 0.98, - "q_hi": 1.0, - "y_target": - [ - 0, 0.3, - 1000, 0.3 - ] - } - ], - "highlight": [ - { - "bound": "LOWER", - "q_lo": 0.98, - "q_hi": 1.0, - "y_target": - [ - 0, 0.3, - 1000, 0.3 - ] - }, - { - "bound": "UPPER", - "q_lo": 0.98, - "q_hi": 1.0, - "y_target": - [ - 0, 0.8, - 1000, 0.8 - ] - } - ], - "shadows": [ - { - "bound": "LOWER", - "q_lo": 0.0, - "q_hi": 0.5, - "y_target": - [ - 0, 0.17, - 1000, 0.17 - ] - } - ] - }, - "y_target": - [ - 0, 0.16, - 1000, 0.165, - 10000, 0.17 - ] - } - }, - { - "rpi.alsc": - { - "omega": 1.3, - "n_iter": 100, - "luminance_strength": 0.5, - "calibrations_Cr": [ - { - "ct": 2960, - "table": - [ - 2.088, 2.086, 2.082, 2.081, 2.077, 2.071, 2.068, 2.068, 2.072, 2.073, 2.075, 2.078, 2.084, 2.092, 2.095, 2.098, - 2.086, 2.084, 2.079, 2.078, 2.075, 2.068, 2.064, 2.063, 2.068, 2.071, 2.072, 2.075, 2.081, 2.089, 2.092, 2.094, - 2.083, 2.081, 2.077, 2.072, 2.069, 2.062, 2.059, 2.059, 2.063, 2.067, 2.069, 2.072, 2.079, 2.088, 2.089, 2.089, - 2.081, 2.077, 2.072, 2.068, 2.065, 2.058, 2.055, 2.054, 2.057, 2.062, 2.066, 2.069, 2.077, 2.084, 2.086, 2.086, - 2.078, 2.075, 2.069, 2.065, 2.061, 2.055, 2.052, 2.049, 2.051, 2.056, 2.062, 2.065, 2.072, 2.079, 2.081, 2.079, - 2.079, 2.075, 2.069, 2.064, 2.061, 2.053, 2.049, 2.046, 2.049, 2.051, 2.057, 2.062, 2.069, 2.075, 2.077, 2.075, - 2.082, 2.079, 2.072, 2.065, 2.061, 2.054, 2.049, 2.047, 2.049, 2.051, 2.056, 2.061, 2.066, 2.073, 2.073, 2.069, - 2.086, 2.082, 2.075, 2.068, 2.062, 2.054, 2.051, 2.049, 2.051, 2.052, 2.056, 2.061, 2.066, 2.073, 2.073, 2.072, - 2.088, 2.086, 2.079, 2.074, 2.066, 2.057, 2.051, 2.051, 2.054, 2.055, 2.056, 2.061, 2.067, 2.072, 2.073, 2.072, - 2.091, 2.087, 2.079, 2.075, 2.068, 2.057, 2.052, 2.052, 2.056, 2.055, 2.055, 2.059, 2.066, 2.072, 2.072, 2.072, - 2.093, 2.088, 2.081, 2.077, 2.069, 2.059, 2.054, 2.054, 2.057, 2.056, 2.056, 2.058, 2.066, 2.072, 2.073, 2.073, - 2.095, 2.091, 2.084, 2.078, 2.075, 2.067, 2.057, 2.057, 2.059, 2.059, 2.058, 2.059, 2.068, 2.073, 2.075, 2.078 - ] - }, - { - "ct": 4850, - "table": - [ - 2.973, 2.968, 2.956, 2.943, 2.941, 2.932, 2.923, 2.921, 2.924, 2.929, 2.931, 2.939, 2.953, 2.965, 2.966, 2.976, - 2.969, 2.962, 2.951, 2.941, 2.934, 2.928, 2.919, 2.918, 2.919, 2.923, 2.927, 2.933, 2.945, 2.957, 2.962, 2.962, - 2.964, 2.956, 2.944, 2.932, 2.929, 2.924, 2.915, 2.914, 2.915, 2.919, 2.924, 2.928, 2.941, 2.952, 2.958, 2.959, - 2.957, 2.951, 2.939, 2.928, 2.924, 2.919, 2.913, 2.911, 2.911, 2.915, 2.919, 2.925, 2.936, 2.947, 2.952, 2.953, - 2.954, 2.947, 2.935, 2.924, 2.919, 2.915, 2.908, 2.906, 2.906, 2.907, 2.914, 2.921, 2.932, 2.941, 2.943, 2.942, - 2.953, 2.946, 2.932, 2.921, 2.916, 2.911, 2.904, 2.902, 2.901, 2.904, 2.909, 2.919, 2.926, 2.937, 2.939, 2.939, - 2.953, 2.947, 2.932, 2.918, 2.915, 2.909, 2.903, 2.901, 2.901, 2.906, 2.911, 2.918, 2.924, 2.936, 2.936, 2.932, - 2.956, 2.948, 2.934, 2.919, 2.916, 2.908, 2.903, 2.901, 2.902, 2.907, 2.909, 2.917, 2.926, 2.936, 2.939, 2.939, - 2.957, 2.951, 2.936, 2.923, 2.917, 2.907, 2.904, 2.901, 2.902, 2.908, 2.911, 2.919, 2.929, 2.939, 2.942, 2.942, - 2.961, 2.951, 2.936, 2.922, 2.918, 2.906, 2.904, 2.901, 2.901, 2.907, 2.911, 2.921, 2.931, 2.941, 2.942, 2.944, - 2.964, 2.954, 2.936, 2.924, 2.918, 2.909, 2.905, 2.905, 2.905, 2.907, 2.912, 2.923, 2.933, 2.942, 2.944, 2.944, - 2.964, 2.958, 2.943, 2.927, 2.921, 2.914, 2.909, 2.907, 2.907, 2.912, 2.916, 2.928, 2.936, 2.944, 2.947, 2.952 - ] - }, - { - "ct": 5930, - "table": - [ - 3.312, 3.308, 3.301, 3.294, 3.288, 3.277, 3.268, 3.261, 3.259, 3.261, 3.267, 3.273, 3.285, 3.301, 3.303, 3.312, - 3.308, 3.304, 3.294, 3.291, 3.283, 3.271, 3.263, 3.259, 3.257, 3.258, 3.261, 3.268, 3.278, 3.293, 3.299, 3.299, - 3.302, 3.296, 3.288, 3.282, 3.276, 3.267, 3.259, 3.254, 3.252, 3.253, 3.256, 3.261, 3.273, 3.289, 3.292, 3.292, - 3.296, 3.289, 3.282, 3.276, 3.269, 3.263, 3.256, 3.251, 3.248, 3.249, 3.251, 3.257, 3.268, 3.279, 3.284, 3.284, - 3.292, 3.285, 3.279, 3.271, 3.264, 3.257, 3.249, 3.243, 3.241, 3.241, 3.246, 3.252, 3.261, 3.274, 3.275, 3.273, - 3.291, 3.285, 3.276, 3.268, 3.259, 3.251, 3.242, 3.239, 3.236, 3.238, 3.244, 3.248, 3.258, 3.268, 3.269, 3.265, - 3.294, 3.288, 3.275, 3.266, 3.257, 3.248, 3.239, 3.238, 3.237, 3.238, 3.243, 3.246, 3.255, 3.264, 3.264, 3.257, - 3.297, 3.293, 3.279, 3.268, 3.258, 3.249, 3.238, 3.237, 3.239, 3.239, 3.243, 3.245, 3.255, 3.264, 3.264, 3.263, - 3.301, 3.295, 3.281, 3.271, 3.259, 3.248, 3.237, 3.237, 3.239, 3.241, 3.243, 3.246, 3.257, 3.265, 3.266, 3.264, - 3.306, 3.295, 3.279, 3.271, 3.261, 3.247, 3.235, 3.234, 3.239, 3.239, 3.243, 3.247, 3.258, 3.265, 3.265, 3.264, - 3.308, 3.297, 3.279, 3.272, 3.261, 3.249, 3.239, 3.239, 3.241, 3.243, 3.245, 3.248, 3.261, 3.265, 3.266, 3.265, - 3.309, 3.301, 3.286, 3.276, 3.267, 3.256, 3.246, 3.242, 3.244, 3.244, 3.249, 3.253, 3.263, 3.267, 3.271, 3.274 - ] - } - ], - "calibrations_Cb": [ - { - "ct": 2960, - "table": - [ - 2.133, 2.134, 2.139, 2.143, 2.148, 2.155, 2.158, 2.158, 2.158, 2.161, 2.161, 2.162, 2.159, 2.156, 2.152, 2.151, - 2.132, 2.133, 2.135, 2.142, 2.147, 2.153, 2.158, 2.158, 2.158, 2.158, 2.159, 2.159, 2.157, 2.154, 2.151, 2.148, - 2.133, 2.133, 2.135, 2.142, 2.149, 2.154, 2.158, 2.158, 2.157, 2.156, 2.158, 2.157, 2.155, 2.153, 2.148, 2.146, - 2.133, 2.133, 2.138, 2.145, 2.149, 2.154, 2.158, 2.159, 2.158, 2.155, 2.157, 2.156, 2.153, 2.149, 2.146, 2.144, - 2.133, 2.134, 2.139, 2.146, 2.149, 2.154, 2.158, 2.159, 2.159, 2.156, 2.154, 2.154, 2.149, 2.145, 2.143, 2.139, - 2.135, 2.135, 2.139, 2.146, 2.151, 2.155, 2.158, 2.159, 2.158, 2.156, 2.153, 2.151, 2.146, 2.143, 2.139, 2.136, - 2.135, 2.135, 2.138, 2.145, 2.151, 2.154, 2.157, 2.158, 2.157, 2.156, 2.153, 2.151, 2.147, 2.143, 2.141, 2.137, - 2.135, 2.134, 2.135, 2.141, 2.149, 2.154, 2.157, 2.157, 2.157, 2.157, 2.157, 2.153, 2.149, 2.146, 2.142, 2.139, - 2.132, 2.133, 2.135, 2.139, 2.148, 2.153, 2.158, 2.159, 2.159, 2.161, 2.161, 2.157, 2.154, 2.149, 2.144, 2.141, - 2.132, 2.133, 2.135, 2.141, 2.149, 2.155, 2.161, 2.161, 2.162, 2.162, 2.163, 2.159, 2.154, 2.149, 2.144, 2.138, - 2.136, 2.136, 2.137, 2.143, 2.149, 2.156, 2.162, 2.163, 2.162, 2.163, 2.164, 2.161, 2.157, 2.152, 2.146, 2.138, - 2.137, 2.137, 2.141, 2.147, 2.152, 2.157, 2.162, 2.162, 2.159, 2.161, 2.162, 2.162, 2.157, 2.152, 2.148, 2.148 - ] - }, - { - "ct": 4850, - "table": - [ - 1.463, 1.464, 1.471, 1.478, 1.479, 1.483, 1.484, 1.486, 1.486, 1.484, 1.483, 1.481, 1.478, 1.475, 1.471, 1.468, - 1.463, 1.463, 1.468, 1.476, 1.479, 1.482, 1.484, 1.487, 1.486, 1.484, 1.483, 1.482, 1.478, 1.473, 1.469, 1.468, - 1.463, 1.464, 1.468, 1.476, 1.479, 1.483, 1.484, 1.486, 1.486, 1.485, 1.484, 1.482, 1.477, 1.473, 1.469, 1.468, - 1.463, 1.464, 1.469, 1.477, 1.481, 1.483, 1.485, 1.487, 1.487, 1.485, 1.485, 1.482, 1.478, 1.474, 1.469, 1.468, - 1.465, 1.465, 1.471, 1.478, 1.481, 1.484, 1.486, 1.488, 1.488, 1.487, 1.485, 1.482, 1.477, 1.472, 1.468, 1.467, - 1.465, 1.466, 1.472, 1.479, 1.482, 1.485, 1.486, 1.488, 1.488, 1.486, 1.484, 1.479, 1.475, 1.472, 1.468, 1.466, - 1.466, 1.466, 1.472, 1.478, 1.482, 1.484, 1.485, 1.488, 1.487, 1.485, 1.483, 1.479, 1.475, 1.472, 1.469, 1.468, - 1.465, 1.466, 1.469, 1.476, 1.481, 1.485, 1.485, 1.486, 1.486, 1.485, 1.483, 1.479, 1.477, 1.474, 1.471, 1.469, - 1.464, 1.465, 1.469, 1.476, 1.481, 1.484, 1.485, 1.487, 1.487, 1.486, 1.485, 1.481, 1.478, 1.475, 1.471, 1.469, - 1.463, 1.464, 1.469, 1.477, 1.481, 1.485, 1.485, 1.488, 1.488, 1.487, 1.486, 1.481, 1.478, 1.475, 1.471, 1.468, - 1.464, 1.465, 1.471, 1.478, 1.482, 1.486, 1.486, 1.488, 1.488, 1.487, 1.486, 1.481, 1.478, 1.475, 1.472, 1.468, - 1.465, 1.466, 1.472, 1.481, 1.483, 1.487, 1.487, 1.488, 1.488, 1.486, 1.485, 1.481, 1.479, 1.476, 1.473, 1.472 - ] - }, - { - "ct": 5930, - "table": - [ - 1.443, 1.444, 1.448, 1.453, 1.459, 1.463, 1.465, 1.467, 1.469, 1.469, 1.467, 1.466, 1.462, 1.457, 1.454, 1.451, - 1.443, 1.444, 1.445, 1.451, 1.459, 1.463, 1.465, 1.467, 1.469, 1.469, 1.467, 1.465, 1.461, 1.456, 1.452, 1.451, - 1.444, 1.444, 1.445, 1.451, 1.459, 1.463, 1.466, 1.468, 1.469, 1.469, 1.467, 1.465, 1.461, 1.456, 1.452, 1.449, - 1.444, 1.444, 1.447, 1.452, 1.459, 1.464, 1.467, 1.469, 1.471, 1.469, 1.467, 1.466, 1.461, 1.456, 1.452, 1.449, - 1.444, 1.445, 1.448, 1.452, 1.459, 1.465, 1.469, 1.471, 1.471, 1.471, 1.468, 1.465, 1.461, 1.455, 1.451, 1.449, - 1.445, 1.446, 1.449, 1.453, 1.461, 1.466, 1.469, 1.471, 1.472, 1.469, 1.467, 1.465, 1.459, 1.455, 1.451, 1.447, - 1.446, 1.446, 1.449, 1.453, 1.461, 1.466, 1.469, 1.469, 1.469, 1.469, 1.467, 1.465, 1.459, 1.455, 1.452, 1.449, - 1.446, 1.446, 1.447, 1.451, 1.459, 1.466, 1.469, 1.469, 1.469, 1.469, 1.467, 1.465, 1.461, 1.457, 1.454, 1.451, - 1.444, 1.444, 1.447, 1.451, 1.459, 1.466, 1.469, 1.469, 1.471, 1.471, 1.468, 1.466, 1.462, 1.458, 1.454, 1.452, - 1.444, 1.444, 1.448, 1.453, 1.459, 1.466, 1.469, 1.471, 1.472, 1.472, 1.468, 1.466, 1.462, 1.458, 1.454, 1.449, - 1.446, 1.447, 1.449, 1.454, 1.461, 1.466, 1.471, 1.471, 1.471, 1.471, 1.468, 1.466, 1.462, 1.459, 1.455, 1.449, - 1.447, 1.447, 1.452, 1.457, 1.462, 1.468, 1.472, 1.472, 1.471, 1.471, 1.468, 1.466, 1.462, 1.459, 1.456, 1.455 - ] - } - ], - "luminance_lut": - [ - 1.548, 1.499, 1.387, 1.289, 1.223, 1.183, 1.164, 1.154, 1.153, 1.169, 1.211, 1.265, 1.345, 1.448, 1.581, 1.619, - 1.513, 1.412, 1.307, 1.228, 1.169, 1.129, 1.105, 1.098, 1.103, 1.127, 1.157, 1.209, 1.272, 1.361, 1.481, 1.583, - 1.449, 1.365, 1.257, 1.175, 1.124, 1.085, 1.062, 1.054, 1.059, 1.079, 1.113, 1.151, 1.211, 1.293, 1.407, 1.488, - 1.424, 1.324, 1.222, 1.139, 1.089, 1.056, 1.034, 1.031, 1.034, 1.049, 1.075, 1.115, 1.164, 1.241, 1.351, 1.446, - 1.412, 1.297, 1.203, 1.119, 1.069, 1.039, 1.021, 1.016, 1.022, 1.032, 1.052, 1.086, 1.135, 1.212, 1.321, 1.439, - 1.406, 1.287, 1.195, 1.115, 1.059, 1.028, 1.014, 1.012, 1.015, 1.026, 1.041, 1.074, 1.125, 1.201, 1.302, 1.425, - 1.406, 1.294, 1.205, 1.126, 1.062, 1.031, 1.013, 1.009, 1.011, 1.019, 1.042, 1.079, 1.129, 1.203, 1.302, 1.435, - 1.415, 1.318, 1.229, 1.146, 1.076, 1.039, 1.019, 1.014, 1.017, 1.031, 1.053, 1.093, 1.144, 1.219, 1.314, 1.436, - 1.435, 1.348, 1.246, 1.164, 1.094, 1.059, 1.036, 1.032, 1.037, 1.049, 1.072, 1.114, 1.167, 1.257, 1.343, 1.462, - 1.471, 1.385, 1.278, 1.189, 1.124, 1.084, 1.064, 1.061, 1.069, 1.078, 1.101, 1.146, 1.207, 1.298, 1.415, 1.496, - 1.522, 1.436, 1.323, 1.228, 1.169, 1.118, 1.101, 1.094, 1.099, 1.113, 1.146, 1.194, 1.265, 1.353, 1.474, 1.571, - 1.578, 1.506, 1.378, 1.281, 1.211, 1.156, 1.135, 1.134, 1.139, 1.158, 1.194, 1.251, 1.327, 1.427, 1.559, 1.611 - ], - "sigma": 0.00121, - "sigma_Cb": 0.00115 - } - }, - { - "rpi.contrast": - { - "ce_enable": 1, - "gamma_curve": - [ - 0, 0, - 1024, 5040, - 2048, 9338, - 3072, 12356, - 4096, 15312, - 5120, 18051, - 6144, 20790, - 7168, 23193, - 8192, 25744, - 9216, 27942, - 10240, 30035, - 11264, 32005, - 12288, 33975, - 13312, 35815, - 14336, 37600, - 15360, 39168, - 16384, 40642, - 18432, 43379, - 20480, 45749, - 22528, 47753, - 24576, 49621, - 26624, 51253, - 28672, 52698, - 30720, 53796, - 32768, 54876, - 36864, 57012, - 40960, 58656, - 45056, 59954, - 49152, 61183, - 53248, 62355, - 57344, 63419, - 61440, 64476, - 65535, 65535 - ] - } - }, - { - "rpi.ccm": - { - "ccms": [ - { - "ct": 2360, - "ccm": - [ - 1.66078, -0.23588, -0.42491, - -0.47456, 1.82763, -0.35307, - -0.00545, -1.44729, 2.45273 - ] - }, - { - "ct": 2870, - "ccm": - [ - 1.78373, -0.55344, -0.23029, - -0.39951, 1.69701, -0.29751, - 0.01986, -1.06525, 2.04539 - ] - }, - { - "ct": 2970, - "ccm": - [ - 1.73511, -0.56973, -0.16537, - -0.36338, 1.69878, -0.33539, - -0.02354, -0.76813, 1.79168 - ] - }, - { - "ct": 3000, - "ccm": - [ - 2.06374, -0.92218, -0.14156, - -0.41721, 1.69289, -0.27568, - -0.00554, -0.92741, 1.93295 - ] - }, - { - "ct": 3700, - "ccm": - [ - 2.13792, -1.08136, -0.05655, - -0.34739, 1.58989, -0.24249, - -0.00349, -0.76789, 1.77138 - ] - }, - { - "ct": 3870, - "ccm": - [ - 1.83834, -0.70528, -0.13307, - -0.30499, 1.60523, -0.30024, - -0.05701, -0.58313, 1.64014 - ] - }, - { - "ct": 4000, - "ccm": - [ - 2.15741, -1.10295, -0.05447, - -0.34631, 1.61158, -0.26528, - -0.02723, -0.70288, 1.73011 - ] - }, - { - "ct": 4400, - "ccm": - [ - 2.05729, -0.95007, -0.10723, - -0.41712, 1.78606, -0.36894, - -0.11899, -0.55727, 1.67626 - ] - }, - { - "ct": 4715, - "ccm": - [ - 1.90255, -0.77478, -0.12777, - -0.31338, 1.88197, -0.56858, - -0.06001, -0.61785, 1.67786 - ] - }, - { - "ct": 5920, - "ccm": - [ - 1.98691, -0.84671, -0.14019, - -0.26581, 1.70615, -0.44035, - -0.09532, -0.47332, 1.56864 - ] - }, - { - "ct": 9050, - "ccm": - [ - 2.09255, -0.76541, -0.32714, - -0.28973, 2.27462, -0.98489, - -0.17299, -0.61275, 1.78574 - ] - } - ] - } - }, - { - "rpi.sharpen": { } - } - ] -} \ No newline at end of file diff --git a/src/ipa/raspberrypi/data/imx519.json b/src/ipa/raspberrypi/data/imx519.json deleted file mode 100644 index 8ccfd3a6..00000000 --- a/src/ipa/raspberrypi/data/imx519.json +++ /dev/null @@ -1,413 +0,0 @@ -{ - "version": 2.0, - "target": "bcm2835", - "algorithms": [ - { - "rpi.black_level": - { - "black_level": 4096 - } - }, - { - "rpi.dpc": { } - }, - { - "rpi.lux": - { - "reference_shutter_speed": 13841, - "reference_gain": 2.0, - "reference_aperture": 1.0, - "reference_lux": 900, - "reference_Y": 12064 - } - }, - { - "rpi.noise": - { - "reference_constant": 0, - "reference_slope": 2.776 - } - }, - { - "rpi.geq": - { - "offset": 189, - "slope": 0.01495 - } - }, - { - "rpi.sdn": { } - }, - { - "rpi.awb": - { - "priors": [ - { - "lux": 0, - "prior": - [ - 2000, 1.0, - 3000, 0.0, - 13000, 0.0 - ] - }, - { - "lux": 800, - "prior": - [ - 2000, 0.0, - 6000, 2.0, - 13000, 2.0 - ] - }, - { - "lux": 1500, - "prior": - [ - 2000, 0.0, - 4000, 1.0, - 6000, 6.0, - 6500, 7.0, - 7000, 1.0, - 13000, 1.0 - ] - } - ], - "modes": - { - "auto": - { - "lo": 2500, - "hi": 7900 - }, - "incandescent": - { - "lo": 2500, - "hi": 3000 - }, - "tungsten": - { - "lo": 3000, - "hi": 3500 - }, - "fluorescent": - { - "lo": 4000, - "hi": 4700 - }, - "indoor": - { - "lo": 3000, - "hi": 5000 - }, - "daylight": - { - "lo": 5500, - "hi": 6500 - }, - "cloudy": - { - "lo": 7000, - "hi": 8000 - } - }, - "bayes": 1, - "ct_curve": - [ - 2890.0, 0.7328, 0.3734, - 3550.0, 0.6228, 0.4763, - 4500.0, 0.5208, 0.5825, - 5700.0, 0.4467, 0.6671, - 7900.0, 0.3858, 0.7411 - ], - "sensitivity_r": 1.0, - "sensitivity_b": 1.0, - "transverse_pos": 0.02027, - "transverse_neg": 0.01935 - } - }, - { - "rpi.agc": - { - "metering_modes": - { - "centre-weighted": - { - "weights": [ 3, 3, 3, 2, 2, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0 ] - }, - "spot": - { - "weights": [ 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ] - }, - "matrix": - { - "weights": [ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ] - } - }, - "exposure_modes": - { - "normal": - { - "shutter": [ 100, 10000, 30000, 60000, 120000 ], - "gain": [ 1.0, 2.0, 4.0, 6.0, 8.0 ] - }, - "short": - { - "shutter": [ 100, 5000, 10000, 20000, 120000 ], - "gain": [ 1.0, 2.0, 4.0, 6.0, 8.0 ] - } - }, - "constraint_modes": - { - "normal": [ - { - "bound": "LOWER", - "q_lo": 0.98, - "q_hi": 1.0, - "y_target": - [ - 0, 0.5, - 1000, 0.5 - ] - } - ], - "highlight": [ - { - "bound": "LOWER", - "q_lo": 0.98, - "q_hi": 1.0, - "y_target": - [ - 0, 0.5, - 1000, 0.5 - ] - }, - { - "bound": "UPPER", - "q_lo": 0.98, - "q_hi": 1.0, - "y_target": - [ - 0, 0.8, - 1000, 0.8 - ] - } - ] - }, - "y_target": - [ - 0, 0.16, - 1000, 0.165, - 10000, 0.17 - ] - } - }, - { - "rpi.alsc": - { - "omega": 1.3, - "n_iter": 100, - "luminance_strength": 0.5, - "calibrations_Cr": [ - { - "ct": 3000, - "table": - [ - 1.527, 1.521, 1.508, 1.493, 1.476, 1.455, 1.442, 1.441, 1.441, 1.441, 1.448, 1.467, 1.483, 1.494, 1.503, 1.504, - 1.525, 1.513, 1.496, 1.477, 1.461, 1.434, 1.418, 1.409, 1.409, 1.416, 1.429, 1.449, 1.469, 1.485, 1.495, 1.503, - 1.517, 1.506, 1.485, 1.461, 1.434, 1.412, 1.388, 1.376, 1.376, 1.386, 1.405, 1.429, 1.449, 1.471, 1.488, 1.495, - 1.512, 1.496, 1.471, 1.442, 1.412, 1.388, 1.361, 1.344, 1.344, 1.358, 1.384, 1.405, 1.431, 1.456, 1.479, 1.489, - 1.508, 1.488, 1.458, 1.425, 1.393, 1.361, 1.343, 1.322, 1.321, 1.342, 1.358, 1.385, 1.416, 1.445, 1.471, 1.484, - 1.507, 1.482, 1.453, 1.418, 1.382, 1.349, 1.322, 1.318, 1.318, 1.321, 1.345, 1.373, 1.405, 1.437, 1.465, 1.483, - 1.507, 1.482, 1.453, 1.418, 1.382, 1.349, 1.322, 1.313, 1.313, 1.321, 1.345, 1.373, 1.405, 1.437, 1.465, 1.483, - 1.507, 1.485, 1.455, 1.422, 1.387, 1.355, 1.333, 1.319, 1.321, 1.333, 1.351, 1.381, 1.411, 1.441, 1.467, 1.483, - 1.508, 1.489, 1.463, 1.432, 1.401, 1.372, 1.355, 1.333, 1.333, 1.351, 1.369, 1.393, 1.422, 1.448, 1.471, 1.484, - 1.511, 1.494, 1.472, 1.444, 1.416, 1.398, 1.372, 1.361, 1.361, 1.369, 1.393, 1.411, 1.436, 1.458, 1.477, 1.487, - 1.511, 1.496, 1.478, 1.455, 1.436, 1.416, 1.399, 1.391, 1.391, 1.397, 1.411, 1.429, 1.451, 1.466, 1.479, 1.487, - 1.511, 1.495, 1.478, 1.462, 1.448, 1.432, 1.419, 1.419, 1.419, 1.419, 1.429, 1.445, 1.459, 1.471, 1.482, 1.487 - ] - }, - { - "ct": 6000, - "table": - [ - 2.581, 2.573, 2.558, 2.539, 2.514, 2.487, 2.473, 2.471, 2.471, 2.471, 2.479, 2.499, 2.517, 2.532, 2.543, 2.544, - 2.575, 2.559, 2.539, 2.521, 2.491, 2.458, 2.435, 2.421, 2.421, 2.429, 2.449, 2.477, 2.499, 2.519, 2.534, 2.543, - 2.561, 2.549, 2.521, 2.491, 2.457, 2.423, 2.393, 2.375, 2.375, 2.387, 2.412, 2.444, 2.475, 2.499, 2.519, 2.532, - 2.552, 2.531, 2.498, 2.459, 2.423, 2.391, 2.349, 2.325, 2.325, 2.344, 2.374, 2.412, 2.444, 2.476, 2.505, 2.519, - 2.543, 2.518, 2.479, 2.435, 2.392, 2.349, 2.324, 2.285, 2.283, 2.313, 2.344, 2.374, 2.417, 2.457, 2.489, 2.506, - 2.541, 2.511, 2.469, 2.421, 2.372, 2.326, 2.284, 2.277, 2.279, 2.283, 2.313, 2.357, 2.401, 2.443, 2.479, 2.504, - 2.541, 2.511, 2.469, 2.421, 2.372, 2.326, 2.284, 2.267, 2.267, 2.281, 2.313, 2.357, 2.401, 2.443, 2.479, 2.504, - 2.541, 2.512, 2.472, 2.425, 2.381, 2.338, 2.302, 2.278, 2.279, 2.301, 2.324, 2.364, 2.407, 2.447, 2.481, 2.504, - 2.544, 2.519, 2.483, 2.441, 2.401, 2.363, 2.338, 2.302, 2.302, 2.324, 2.355, 2.385, 2.423, 2.459, 2.488, 2.506, - 2.549, 2.527, 2.497, 2.463, 2.427, 2.401, 2.363, 2.345, 2.345, 2.355, 2.385, 2.412, 2.444, 2.473, 2.497, 2.509, - 2.552, 2.532, 2.507, 2.481, 2.459, 2.427, 2.402, 2.389, 2.389, 2.394, 2.412, 2.444, 2.465, 2.481, 2.499, 2.511, - 2.553, 2.533, 2.508, 2.489, 2.475, 2.454, 2.429, 2.429, 2.429, 2.429, 2.439, 2.463, 2.481, 2.492, 2.504, 2.511 - ] - } - ], - "calibrations_Cb": [ - { - "ct": 3000, - "table": - [ - 3.132, 3.126, 3.116, 3.103, 3.097, 3.091, 3.087, 3.086, 3.088, 3.091, 3.092, 3.102, 3.113, 3.121, 3.141, 3.144, - 3.149, 3.132, 3.123, 3.108, 3.101, 3.096, 3.091, 3.089, 3.091, 3.092, 3.101, 3.107, 3.116, 3.129, 3.144, 3.153, - 3.161, 3.149, 3.129, 3.121, 3.108, 3.103, 3.101, 3.101, 3.101, 3.103, 3.107, 3.116, 3.125, 3.134, 3.153, 3.159, - 3.176, 3.161, 3.144, 3.129, 3.124, 3.121, 3.117, 3.118, 3.118, 3.119, 3.122, 3.125, 3.134, 3.146, 3.159, 3.171, - 3.183, 3.176, 3.157, 3.144, 3.143, 3.143, 3.139, 3.141, 3.141, 3.141, 3.141, 3.141, 3.146, 3.161, 3.171, 3.179, - 3.189, 3.183, 3.165, 3.157, 3.156, 3.157, 3.159, 3.163, 3.163, 3.163, 3.163, 3.161, 3.163, 3.169, 3.179, 3.187, - 3.199, 3.189, 3.171, 3.165, 3.164, 3.167, 3.171, 3.173, 3.173, 3.172, 3.171, 3.169, 3.169, 3.175, 3.187, 3.189, - 3.206, 3.196, 3.177, 3.171, 3.165, 3.167, 3.171, 3.173, 3.173, 3.172, 3.171, 3.171, 3.173, 3.177, 3.192, 3.194, - 3.209, 3.197, 3.178, 3.171, 3.164, 3.161, 3.159, 3.161, 3.162, 3.164, 3.167, 3.171, 3.173, 3.181, 3.193, 3.198, - 3.204, 3.194, 3.176, 3.165, 3.161, 3.156, 3.154, 3.154, 3.159, 3.161, 3.164, 3.168, 3.173, 3.182, 3.198, 3.199, - 3.199, 3.191, 3.176, 3.169, 3.161, 3.157, 3.153, 3.153, 3.156, 3.161, 3.164, 3.168, 3.173, 3.186, 3.196, 3.199, - 3.199, 3.188, 3.179, 3.173, 3.165, 3.157, 3.153, 3.154, 3.156, 3.159, 3.167, 3.171, 3.176, 3.185, 3.193, 3.198 - ] - }, - { - "ct": 6000, - "table": - [ - 1.579, 1.579, 1.577, 1.574, 1.573, 1.571, 1.571, 1.571, 1.571, 1.569, 1.569, 1.571, 1.572, 1.574, 1.577, 1.578, - 1.584, 1.579, 1.578, 1.575, 1.573, 1.572, 1.571, 1.572, 1.572, 1.571, 1.571, 1.572, 1.573, 1.576, 1.578, 1.579, - 1.587, 1.584, 1.579, 1.578, 1.575, 1.573, 1.573, 1.575, 1.575, 1.574, 1.573, 1.574, 1.576, 1.578, 1.581, 1.581, - 1.591, 1.587, 1.584, 1.579, 1.578, 1.579, 1.579, 1.581, 1.581, 1.581, 1.578, 1.577, 1.578, 1.581, 1.585, 1.586, - 1.595, 1.591, 1.587, 1.585, 1.585, 1.586, 1.587, 1.587, 1.588, 1.588, 1.585, 1.584, 1.584, 1.586, 1.589, 1.589, - 1.597, 1.595, 1.591, 1.589, 1.591, 1.593, 1.595, 1.596, 1.597, 1.597, 1.595, 1.594, 1.592, 1.592, 1.593, 1.593, - 1.601, 1.597, 1.593, 1.592, 1.593, 1.595, 1.598, 1.599, 1.602, 1.601, 1.598, 1.596, 1.595, 1.596, 1.595, 1.595, - 1.601, 1.599, 1.594, 1.593, 1.593, 1.595, 1.598, 1.599, 1.602, 1.601, 1.598, 1.597, 1.597, 1.597, 1.597, 1.597, - 1.602, 1.599, 1.594, 1.593, 1.592, 1.593, 1.595, 1.597, 1.597, 1.598, 1.598, 1.597, 1.597, 1.597, 1.598, 1.598, - 1.599, 1.598, 1.594, 1.592, 1.591, 1.591, 1.592, 1.595, 1.596, 1.597, 1.597, 1.597, 1.597, 1.599, 1.599, 1.599, - 1.598, 1.596, 1.594, 1.593, 1.592, 1.592, 1.592, 1.594, 1.595, 1.597, 1.597, 1.597, 1.598, 1.599, 1.599, 1.599, - 1.597, 1.595, 1.594, 1.594, 1.593, 1.592, 1.593, 1.595, 1.595, 1.597, 1.598, 1.598, 1.598, 1.599, 1.599, 1.599 - ] - } - ], - "luminance_lut": - [ - 2.887, 2.754, 2.381, 2.105, 1.859, 1.678, 1.625, 1.623, 1.623, 1.624, 1.669, 1.849, 2.092, 2.362, 2.723, 2.838, - 2.754, 2.443, 2.111, 1.905, 1.678, 1.542, 1.455, 1.412, 1.412, 1.452, 1.535, 1.665, 1.893, 2.096, 2.413, 2.723, - 2.443, 2.216, 1.911, 1.678, 1.537, 1.372, 1.288, 1.245, 1.245, 1.283, 1.363, 1.527, 1.665, 1.895, 2.193, 2.413, - 2.318, 2.057, 1.764, 1.541, 1.372, 1.282, 1.159, 1.113, 1.113, 1.151, 1.269, 1.363, 1.527, 1.749, 2.034, 2.278, - 2.259, 1.953, 1.671, 1.452, 1.283, 1.159, 1.107, 1.018, 1.017, 1.097, 1.151, 1.269, 1.437, 1.655, 1.931, 2.222, - 2.257, 1.902, 1.624, 1.408, 1.239, 1.111, 1.019, 1.011, 1.005, 1.014, 1.098, 1.227, 1.395, 1.608, 1.883, 2.222, - 2.257, 1.902, 1.624, 1.408, 1.239, 1.111, 1.016, 1.001, 1.001, 1.007, 1.098, 1.227, 1.395, 1.608, 1.883, 2.222, - 2.257, 1.946, 1.666, 1.448, 1.281, 1.153, 1.093, 1.013, 1.008, 1.089, 1.143, 1.269, 1.437, 1.654, 1.934, 2.226, - 2.309, 2.044, 1.756, 1.532, 1.363, 1.259, 1.153, 1.093, 1.093, 1.143, 1.264, 1.354, 1.524, 1.746, 2.035, 2.284, - 2.425, 2.201, 1.896, 1.662, 1.519, 1.363, 1.259, 1.214, 1.214, 1.264, 1.354, 1.519, 1.655, 1.888, 2.191, 2.413, - 2.724, 2.417, 2.091, 1.888, 1.662, 1.519, 1.419, 1.373, 1.373, 1.425, 1.521, 1.655, 1.885, 2.089, 2.409, 2.722, - 2.858, 2.724, 2.356, 2.085, 1.842, 1.658, 1.581, 1.577, 1.577, 1.579, 1.653, 1.838, 2.084, 2.359, 2.722, 2.842 - ], - "sigma": 0.00372, - "sigma_Cb": 0.00244 - } - }, - { - "rpi.contrast": - { - "ce_enable": 1, - "gamma_curve": - [ - 0, 0, - 1024, 5040, - 2048, 9338, - 3072, 12356, - 4096, 15312, - 5120, 18051, - 6144, 20790, - 7168, 23193, - 8192, 25744, - 9216, 27942, - 10240, 30035, - 11264, 32005, - 12288, 33975, - 13312, 35815, - 14336, 37600, - 15360, 39168, - 16384, 40642, - 18432, 43379, - 20480, 45749, - 22528, 47753, - 24576, 49621, - 26624, 51253, - 28672, 52698, - 30720, 53796, - 32768, 54876, - 36864, 57012, - 40960, 58656, - 45056, 59954, - 49152, 61183, - 53248, 62355, - 57344, 63419, - 61440, 64476, - 65535, 65535 - ] - } - }, - { - "rpi.ccm": - { - "ccms": [ - { - "ct": 2890, - "ccm": - [ - 1.36754, -0.18448, -0.18306, - -0.32356, 1.44826, -0.12471, - -0.00412, -0.69936, 1.70348 - ] - }, - { - "ct": 2920, - "ccm": - [ - 1.26704, 0.01624, -0.28328, - -0.28516, 1.38934, -0.10419, - -0.04854, -0.82211, 1.87066 - ] - }, - { - "ct": 3550, - "ccm": - [ - 1.42836, -0.27235, -0.15601, - -0.28751, 1.41075, -0.12325, - -0.01812, -0.54849, 1.56661 - ] - }, - { - "ct": 4500, - "ccm": - [ - 1.36328, -0.19569, -0.16759, - -0.25254, 1.52248, -0.26994, - -0.01575, -0.53155, 1.54729 - ] - }, - { - "ct": 5700, - "ccm": - [ - 1.49207, -0.37245, -0.11963, - -0.21493, 1.40005, -0.18512, - -0.03781, -0.38779, 1.42561 - ] - }, - { - "ct": 7900, - "ccm": - [ - 1.34849, -0.05425, -0.29424, - -0.22182, 1.77684, -0.55502, - -0.07403, -0.55336, 1.62739 - ] - } - ] - } - }, - { - "rpi.sharpen": { } - } - ] -} \ No newline at end of file diff --git a/src/ipa/raspberrypi/data/imx708.json b/src/ipa/raspberrypi/data/imx708.json deleted file mode 100644 index b9830a3b..00000000 --- a/src/ipa/raspberrypi/data/imx708.json +++ /dev/null @@ -1,556 +0,0 @@ -{ - "version": 2.0, - "target": "bcm2835", - "algorithms": [ - { - "rpi.black_level": - { - "black_level": 4096 - } - }, - { - "rpi.dpc": { } - }, - { - "rpi.lux": - { - "reference_shutter_speed": 10672, - "reference_gain": 1.12, - "reference_aperture": 1.0, - "reference_lux": 977, - "reference_Y": 8627 - } - }, - { - "rpi.noise": - { - "reference_constant": 16.0, - "reference_slope": 4.0 - } - }, - { - "rpi.geq": - { - "offset": 215, - "slope": 0.00287 - } - }, - { - "rpi.sdn": { } - }, - { - "rpi.awb": - { - "priors": [ - { - "lux": 0, - "prior": - [ - 2000, 1.0, - 3000, 0.0, - 13000, 0.0 - ] - }, - { - "lux": 800, - "prior": - [ - 2000, 0.0, - 6000, 2.0, - 13000, 2.0 - ] - }, - { - "lux": 1500, - "prior": - [ - 2000, 0.0, - 4000, 1.0, - 6000, 6.0, - 6500, 7.0, - 7000, 1.0, - 13000, 1.0 - ] - } - ], - "modes": - { - "auto": - { - "lo": 2500, - "hi": 8000 - }, - "incandescent": - { - "lo": 2500, - "hi": 3000 - }, - "tungsten": - { - "lo": 3000, - "hi": 3500 - }, - "fluorescent": - { - "lo": 4000, - "hi": 4700 - }, - "indoor": - { - "lo": 3000, - "hi": 5000 - }, - "daylight": - { - "lo": 5500, - "hi": 6500 - }, - "cloudy": - { - "lo": 7000, - "hi": 8600 - } - }, - "bayes": 1, - "ct_curve": - [ - 2498.0, 0.8733, 0.2606, - 2821.0, 0.7707, 0.3245, - 2925.0, 0.7338, 0.3499, - 2926.0, 0.7193, 0.3603, - 2951.0, 0.7144, 0.3639, - 2954.0, 0.7111, 0.3663, - 3578.0, 0.6038, 0.4516, - 3717.0, 0.5861, 0.4669, - 3784.0, 0.5786, 0.4737, - 4485.0, 0.5113, 0.5368, - 4615.0, 0.4994, 0.5486, - 4671.0, 0.4927, 0.5554, - 5753.0, 0.4274, 0.6246, - 5773.0, 0.4265, 0.6256, - 7433.0, 0.3723, 0.6881 - ], - "sensitivity_r": 1.05, - "sensitivity_b": 1.05, - "transverse_pos": 0.03148, - "transverse_neg": 0.03061 - } - }, - { - "rpi.agc": - { - "metering_modes": - { - "centre-weighted": - { - "weights": [ 3, 3, 3, 2, 2, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0 ] - }, - "spot": - { - "weights": [ 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ] - }, - "matrix": - { - "weights": [ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ] - } - }, - "exposure_modes": - { - "normal": - { - "shutter": [ 100, 15000, 30000, 60000, 120000 ], - "gain": [ 1.0, 1.0, 2.0, 4.0, 6.0 ] - }, - "short": - { - "shutter": [ 100, 5000, 10000, 20000, 120000 ], - "gain": [ 1.0, 2.0, 4.0, 6.0, 6.0 ] - } - }, - "constraint_modes": - { - "normal": [ - { - "bound": "LOWER", - "q_lo": 0.98, - "q_hi": 1.0, - "y_target": - [ - 0, 0.2, - 1000, 0.2 - ] - } - ], - "highlight": [ - { - "bound": "LOWER", - "q_lo": 0.98, - "q_hi": 1.0, - "y_target": - [ - 0, 0.2, - 1000, 0.2 - ] - }, - { - "bound": "UPPER", - "q_lo": 0.98, - "q_hi": 1.0, - "y_target": - [ - 0, 0.8, - 1000, 0.8 - ] - } - ] - }, - "y_target": - [ - 0, 0.16, - 1000, 0.165, - 10000, 0.17 - ], - "startup_frames": 5, - "convergence_frames": 6, - "speed": 0.15 - } - }, - { - "rpi.alsc": - { - "omega": 1.3, - "n_iter": 100, - "luminance_strength": 0.5, - "calibrations_Cr": [ - { - "ct": 3000, - "table": - [ - 1.562, 1.566, 1.566, 1.556, 1.533, 1.506, 1.475, 1.475, 1.475, 1.475, 1.506, 1.533, 1.555, 1.563, 1.562, 1.555, - 1.563, 1.564, 1.561, 1.538, 1.508, 1.482, 1.449, 1.436, 1.436, 1.449, 1.481, 1.508, 1.537, 1.557, 1.558, 1.557, - 1.564, 1.563, 1.554, 1.522, 1.482, 1.449, 1.421, 1.403, 1.403, 1.419, 1.449, 1.481, 1.519, 1.549, 1.557, 1.559, - 1.564, 1.563, 1.545, 1.506, 1.462, 1.421, 1.403, 1.378, 1.378, 1.402, 1.419, 1.459, 1.503, 1.541, 1.557, 1.559, - 1.564, 1.562, 1.537, 1.494, 1.447, 1.404, 1.378, 1.364, 1.364, 1.377, 1.402, 1.444, 1.491, 1.532, 1.556, 1.559, - 1.564, 1.559, 1.532, 1.487, 1.438, 1.395, 1.365, 1.359, 1.359, 1.364, 1.393, 1.436, 1.484, 1.527, 1.555, 1.558, - 1.564, 1.559, 1.532, 1.487, 1.438, 1.395, 1.365, 1.356, 1.356, 1.364, 1.393, 1.436, 1.484, 1.527, 1.554, 1.557, - 1.564, 1.561, 1.536, 1.492, 1.444, 1.402, 1.374, 1.364, 1.363, 1.373, 1.401, 1.442, 1.489, 1.531, 1.554, 1.557, - 1.564, 1.563, 1.544, 1.504, 1.458, 1.418, 1.397, 1.374, 1.374, 1.395, 1.416, 1.456, 1.501, 1.538, 1.556, 1.557, - 1.564, 1.562, 1.551, 1.518, 1.477, 1.441, 1.418, 1.397, 1.397, 1.416, 1.438, 1.474, 1.514, 1.546, 1.556, 1.556, - 1.562, 1.562, 1.558, 1.534, 1.499, 1.476, 1.441, 1.426, 1.426, 1.438, 1.473, 1.496, 1.531, 1.552, 1.556, 1.555, - 1.561, 1.564, 1.564, 1.552, 1.525, 1.497, 1.466, 1.461, 1.461, 1.464, 1.495, 1.523, 1.548, 1.556, 1.556, 1.552 - ] - }, - { - "ct": 5000, - "table": - [ - 2.609, 2.616, 2.617, 2.607, 2.573, 2.527, 2.483, 2.481, 2.481, 2.483, 2.529, 2.573, 2.604, 2.613, 2.613, 2.604, - 2.609, 2.615, 2.608, 2.576, 2.533, 2.489, 2.439, 2.418, 2.418, 2.439, 2.491, 2.532, 2.577, 2.605, 2.609, 2.607, - 2.611, 2.611, 2.597, 2.551, 2.489, 2.439, 2.391, 2.364, 2.364, 2.391, 2.439, 2.491, 2.551, 2.592, 2.607, 2.609, - 2.612, 2.608, 2.583, 2.526, 2.457, 2.391, 2.362, 2.318, 2.318, 2.362, 2.391, 2.458, 2.526, 2.581, 2.607, 2.611, - 2.612, 2.604, 2.571, 2.507, 2.435, 2.362, 2.317, 2.293, 2.294, 2.318, 2.363, 2.434, 2.508, 2.568, 2.604, 2.612, - 2.611, 2.602, 2.564, 2.496, 2.419, 2.349, 2.293, 2.284, 2.284, 2.294, 2.347, 2.421, 2.497, 2.562, 2.603, 2.611, - 2.609, 2.601, 2.564, 2.496, 2.419, 2.349, 2.293, 2.278, 2.278, 2.294, 2.347, 2.421, 2.497, 2.562, 2.602, 2.609, - 2.609, 2.602, 2.568, 2.503, 2.429, 2.361, 2.311, 2.292, 2.292, 2.309, 2.357, 2.429, 2.504, 2.567, 2.602, 2.609, - 2.606, 2.604, 2.579, 2.519, 2.449, 2.384, 2.348, 2.311, 2.311, 2.346, 2.383, 2.449, 2.521, 2.577, 2.604, 2.608, - 2.604, 2.603, 2.586, 2.537, 2.474, 2.418, 2.384, 2.348, 2.348, 2.383, 2.417, 2.476, 2.538, 2.586, 2.601, 2.603, - 2.603, 2.605, 2.596, 2.561, 2.508, 2.474, 2.418, 2.396, 2.396, 2.417, 2.474, 2.511, 2.562, 2.596, 2.603, 2.602, - 2.601, 2.607, 2.606, 2.589, 2.549, 2.507, 2.456, 2.454, 2.454, 2.458, 2.508, 2.554, 2.594, 2.605, 2.605, 2.602 - ] - } - ], - "calibrations_Cb": [ - { - "ct": 3000, - "table": - [ - 3.221, 3.226, 3.231, 3.236, 3.239, 3.243, 3.245, 3.247, 3.249, 3.253, 3.255, 3.254, 3.253, 3.242, 3.235, 3.226, - 3.225, 3.231, 3.235, 3.238, 3.241, 3.244, 3.246, 3.247, 3.249, 3.254, 3.256, 3.255, 3.252, 3.248, 3.241, 3.232, - 3.226, 3.234, 3.239, 3.243, 3.243, 3.245, 3.247, 3.248, 3.251, 3.255, 3.256, 3.256, 3.254, 3.249, 3.244, 3.236, - 3.232, 3.238, 3.245, 3.245, 3.246, 3.247, 3.248, 3.251, 3.251, 3.256, 3.257, 3.257, 3.256, 3.254, 3.249, 3.239, - 3.232, 3.243, 3.246, 3.246, 3.246, 3.247, 3.248, 3.251, 3.253, 3.257, 3.258, 3.258, 3.257, 3.256, 3.254, 3.239, - 3.232, 3.242, 3.246, 3.247, 3.246, 3.246, 3.248, 3.251, 3.252, 3.253, 3.256, 3.255, 3.255, 3.254, 3.251, 3.239, - 3.233, 3.241, 3.244, 3.245, 3.244, 3.245, 3.246, 3.249, 3.251, 3.252, 3.253, 3.252, 3.252, 3.252, 3.249, 3.238, - 3.238, 3.241, 3.246, 3.246, 3.245, 3.245, 3.247, 3.249, 3.251, 3.252, 3.253, 3.253, 3.252, 3.252, 3.249, 3.239, - 3.235, 3.241, 3.245, 3.245, 3.245, 3.245, 3.246, 3.247, 3.251, 3.254, 3.253, 3.255, 3.256, 3.255, 3.251, 3.241, - 3.226, 3.235, 3.241, 3.241, 3.241, 3.241, 3.243, 3.245, 3.246, 3.252, 3.253, 3.254, 3.256, 3.254, 3.241, 3.237, - 3.205, 3.213, 3.213, 3.214, 3.214, 3.214, 3.214, 3.213, 3.213, 3.216, 3.218, 3.216, 3.214, 3.213, 3.211, 3.208, - 3.205, 3.205, 3.212, 3.212, 3.212, 3.213, 3.211, 3.211, 3.211, 3.213, 3.216, 3.214, 3.213, 3.211, 3.208, 3.196 - ] - }, - { - "ct": 5000, - "table": - [ - 1.645, 1.646, 1.649, 1.653, 1.654, 1.657, 1.659, 1.661, 1.663, 1.662, 1.661, 1.659, 1.656, 1.651, 1.645, 1.642, - 1.646, 1.649, 1.652, 1.654, 1.656, 1.659, 1.662, 1.663, 1.664, 1.664, 1.662, 1.661, 1.657, 1.653, 1.649, 1.644, - 1.648, 1.652, 1.654, 1.656, 1.658, 1.662, 1.665, 1.668, 1.668, 1.668, 1.665, 1.662, 1.658, 1.655, 1.652, 1.646, - 1.649, 1.653, 1.656, 1.658, 1.661, 1.665, 1.667, 1.671, 1.673, 1.671, 1.668, 1.663, 1.659, 1.656, 1.654, 1.647, - 1.649, 1.655, 1.657, 1.659, 1.661, 1.666, 1.671, 1.674, 1.675, 1.673, 1.671, 1.664, 1.659, 1.656, 1.654, 1.648, - 1.649, 1.654, 1.656, 1.659, 1.661, 1.666, 1.673, 1.676, 1.676, 1.675, 1.671, 1.664, 1.659, 1.656, 1.654, 1.648, - 1.649, 1.654, 1.656, 1.658, 1.659, 1.665, 1.672, 1.675, 1.675, 1.674, 1.668, 1.662, 1.658, 1.655, 1.654, 1.646, - 1.652, 1.655, 1.657, 1.659, 1.661, 1.665, 1.671, 1.673, 1.673, 1.672, 1.668, 1.662, 1.658, 1.655, 1.654, 1.647, - 1.652, 1.655, 1.657, 1.659, 1.661, 1.664, 1.667, 1.671, 1.672, 1.668, 1.666, 1.662, 1.659, 1.656, 1.654, 1.647, - 1.647, 1.652, 1.655, 1.656, 1.657, 1.661, 1.664, 1.665, 1.665, 1.665, 1.663, 1.661, 1.657, 1.655, 1.647, 1.647, - 1.639, 1.642, 1.644, 1.645, 1.646, 1.648, 1.648, 1.648, 1.649, 1.649, 1.649, 1.646, 1.645, 1.642, 1.639, 1.636, - 1.639, 1.641, 1.642, 1.644, 1.645, 1.646, 1.647, 1.647, 1.648, 1.648, 1.647, 1.645, 1.642, 1.639, 1.636, 1.633 - ] - } - ], - "luminance_lut": - [ - 2.644, 2.396, 2.077, 1.863, 1.682, 1.535, 1.392, 1.382, 1.382, 1.382, 1.515, 1.657, 1.826, 2.035, 2.351, 2.604, - 2.497, 2.229, 1.947, 1.733, 1.539, 1.424, 1.296, 1.249, 1.249, 1.285, 1.401, 1.519, 1.699, 1.908, 2.183, 2.456, - 2.389, 2.109, 1.848, 1.622, 1.424, 1.296, 1.201, 1.146, 1.146, 1.188, 1.285, 1.401, 1.591, 1.811, 2.065, 2.347, - 2.317, 2.026, 1.771, 1.535, 1.339, 1.201, 1.145, 1.069, 1.069, 1.134, 1.188, 1.318, 1.505, 1.734, 1.983, 2.273, - 2.276, 1.972, 1.715, 1.474, 1.281, 1.148, 1.069, 1.033, 1.024, 1.065, 1.134, 1.262, 1.446, 1.679, 1.929, 2.233, - 2.268, 1.941, 1.682, 1.441, 1.251, 1.119, 1.033, 1.013, 1.013, 1.024, 1.105, 1.231, 1.415, 1.649, 1.898, 2.227, - 2.268, 1.941, 1.682, 1.441, 1.251, 1.119, 1.033, 1.001, 1.001, 1.024, 1.105, 1.231, 1.415, 1.649, 1.898, 2.227, - 2.268, 1.951, 1.694, 1.456, 1.265, 1.131, 1.044, 1.026, 1.019, 1.039, 1.118, 1.246, 1.429, 1.663, 1.912, 2.227, - 2.291, 1.992, 1.738, 1.505, 1.311, 1.175, 1.108, 1.044, 1.041, 1.106, 1.161, 1.292, 1.478, 1.707, 1.955, 2.252, - 2.347, 2.058, 1.803, 1.581, 1.384, 1.245, 1.175, 1.108, 1.108, 1.161, 1.239, 1.364, 1.551, 1.773, 2.023, 2.311, - 2.438, 2.156, 1.884, 1.674, 1.484, 1.373, 1.245, 1.199, 1.199, 1.239, 1.363, 1.463, 1.647, 1.858, 2.123, 2.406, - 2.563, 2.305, 1.998, 1.792, 1.615, 1.472, 1.339, 1.322, 1.322, 1.326, 1.456, 1.593, 1.767, 1.973, 2.273, 2.532 - ], - "sigma": 0.00178, - "sigma_Cb": 0.00217 - } - }, - { - "rpi.contrast": - { - "ce_enable": 1, - "gamma_curve": - [ - 0, 0, - 1024, 5040, - 2048, 9338, - 3072, 12356, - 4096, 15312, - 5120, 18051, - 6144, 20790, - 7168, 23193, - 8192, 25744, - 9216, 27942, - 10240, 30035, - 11264, 32005, - 12288, 33975, - 13312, 35815, - 14336, 37600, - 15360, 39168, - 16384, 40642, - 18432, 43379, - 20480, 45749, - 22528, 47753, - 24576, 49621, - 26624, 51253, - 28672, 52698, - 30720, 53796, - 32768, 54876, - 36864, 57012, - 40960, 58656, - 45056, 59954, - 49152, 61183, - 53248, 62355, - 57344, 63419, - 61440, 64476, - 65535, 65535 - ] - } - }, - { - "rpi.ccm": - { - "ccms": [ - { - "ct": 2498, - "ccm": - [ - 1.14912, 0.28638, -0.43551, - -0.49691, 1.60391, -0.10701, - -0.10513, -1.09534, 2.20047 - ] - }, - { - "ct": 2821, - "ccm": - [ - 1.18251, 0.15501, -0.33752, - -0.44304, 1.58495, -0.14191, - -0.05077, -0.96422, 2.01498 - ] - }, - { - "ct": 2925, - "ccm": - [ - 1.18668, 0.00195, -0.18864, - -0.41617, 1.50514, -0.08897, - -0.02675, -0.91143, 1.93818 - ] - }, - { - "ct": 2926, - "ccm": - [ - 1.50948, -0.44421, -0.06527, - -0.37241, 1.41726, -0.04486, - 0.07098, -0.84694, 1.77596 - ] - }, - { - "ct": 2951, - "ccm": - [ - 1.52743, -0.47333, -0.05411, - -0.36485, 1.40764, -0.04279, - 0.08672, -0.90479, 1.81807 - ] - }, - { - "ct": 2954, - "ccm": - [ - 1.51683, -0.46841, -0.04841, - -0.36288, 1.39914, -0.03625, - 0.06421, -0.82034, 1.75613 - ] - }, - { - "ct": 3578, - "ccm": - [ - 1.59888, -0.59105, -0.00784, - -0.29366, 1.32037, -0.02671, - 0.06627, -0.76465, 1.69838 - ] - }, - { - "ct": 3717, - "ccm": - [ - 1.59063, -0.58059, -0.01003, - -0.29583, 1.32715, -0.03132, - 0.03613, -0.67431, 1.63817 - ] - }, - { - "ct": 3784, - "ccm": - [ - 1.59379, -0.58861, -0.00517, - -0.29178, 1.33292, -0.04115, - 0.03541, -0.66162, 1.62622 - ] - }, - { - "ct": 4485, - "ccm": - [ - 1.40761, -0.34561, -0.06201, - -0.32388, 1.57221, -0.24832, - -0.01014, -0.63427, 1.64441 - ] - }, - { - "ct": 4615, - "ccm": - [ - 1.41537, -0.35832, -0.05705, - -0.31429, 1.56019, -0.24591, - -0.01761, -0.61859, 1.63621 - ] - }, - { - "ct": 4671, - "ccm": - [ - 1.42941, -0.38178, -0.04764, - -0.31421, 1.55925, -0.24504, - -0.01141, -0.62987, 1.64129 - ] - }, - { - "ct": 5753, - "ccm": - [ - 1.64549, -0.63329, -0.01221, - -0.22431, 1.36423, -0.13992, - -0.00831, -0.55373, 1.56204 - ] - }, - { - "ct": 5773, - "ccm": - [ - 1.63668, -0.63557, -0.00111, - -0.21919, 1.36234, -0.14315, - -0.00399, -0.57428, 1.57827 - ] - }, - { - "ct": 7433, - "ccm": - [ - 1.36007, -0.09277, -0.26729, - -0.36886, 2.09249, -0.72363, - -0.12573, -0.76761, 1.89334 - ] - }, - { - "ct": 55792, - "ccm": - [ - 1.65091, -0.63689, -0.01401, - -0.22277, 1.35752, -0.13475, - -0.00943, -0.55091, 1.56033 - ] - } - ] - } - }, - { - "rpi.sharpen": { } - }, - { - "rpi.af": - { - "ranges": - { - "normal": - { - "min": 0.0, - "max": 12.0, - "default": 1.0 - }, - "macro": - { - "min": 3.0, - "max": 15.0, - "default": 4.0 - } - }, - "speeds": - { - "normal": - { - "step_coarse": 1.0, - "step_fine": 0.25, - "contrast_ratio": 0.75, - "pdaf_gain": -0.02, - "pdaf_squelch": 0.125, - "max_slew": 2.0, - "pdaf_frames": 20, - "dropout_frames": 6, - "step_frames": 4 - } - }, - "conf_epsilon": 8, - "conf_thresh": 16, - "conf_clip": 512, - "skip_frames": 5, - "map": [ 0.0, 445, 15.0, 925 ] - } - } - ] -} diff --git a/src/ipa/raspberrypi/data/imx708_noir.json b/src/ipa/raspberrypi/data/imx708_noir.json deleted file mode 100644 index 075f7035..00000000 --- a/src/ipa/raspberrypi/data/imx708_noir.json +++ /dev/null @@ -1,556 +0,0 @@ -{ - "version": 2.0, - "target": "bcm2835", - "algorithms": [ - { - "rpi.black_level": - { - "black_level": 4096 - } - }, - { - "rpi.dpc": { } - }, - { - "rpi.lux": - { - "reference_shutter_speed": 10672, - "reference_gain": 1.12, - "reference_aperture": 1.0, - "reference_lux": 977, - "reference_Y": 8627 - } - }, - { - "rpi.noise": - { - "reference_constant": 16.0, - "reference_slope": 4.0 - } - }, - { - "rpi.geq": - { - "offset": 215, - "slope": 0.00287 - } - }, - { - "rpi.sdn": { } - }, - { - "rpi.awb": - { - "priors": [ - { - "lux": 0, - "prior": - [ - 2000, 1.0, - 3000, 0.0, - 13000, 0.0 - ] - }, - { - "lux": 800, - "prior": - [ - 2000, 0.0, - 6000, 2.0, - 13000, 2.0 - ] - }, - { - "lux": 1500, - "prior": - [ - 2000, 0.0, - 4000, 1.0, - 6000, 6.0, - 6500, 7.0, - 7000, 1.0, - 13000, 1.0 - ] - } - ], - "modes": - { - "auto": - { - "lo": 2500, - "hi": 8000 - }, - "incandescent": - { - "lo": 2500, - "hi": 3000 - }, - "tungsten": - { - "lo": 3000, - "hi": 3500 - }, - "fluorescent": - { - "lo": 4000, - "hi": 4700 - }, - "indoor": - { - "lo": 3000, - "hi": 5000 - }, - "daylight": - { - "lo": 5500, - "hi": 6500 - }, - "cloudy": - { - "lo": 7000, - "hi": 8600 - } - }, - "bayes": 0, - "ct_curve": - [ - 2498.0, 0.8733, 0.2606, - 2821.0, 0.7707, 0.3245, - 2925.0, 0.7338, 0.3499, - 2926.0, 0.7193, 0.3603, - 2951.0, 0.7144, 0.3639, - 2954.0, 0.7111, 0.3663, - 3578.0, 0.6038, 0.4516, - 3717.0, 0.5861, 0.4669, - 3784.0, 0.5786, 0.4737, - 4485.0, 0.5113, 0.5368, - 4615.0, 0.4994, 0.5486, - 4671.0, 0.4927, 0.5554, - 5753.0, 0.4274, 0.6246, - 5773.0, 0.4265, 0.6256, - 7433.0, 0.3723, 0.6881 - ], - "sensitivity_r": 1.05, - "sensitivity_b": 1.05, - "transverse_pos": 0.03148, - "transverse_neg": 0.03061 - } - }, - { - "rpi.agc": - { - "metering_modes": - { - "centre-weighted": - { - "weights": [ 3, 3, 3, 2, 2, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0 ] - }, - "spot": - { - "weights": [ 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ] - }, - "matrix": - { - "weights": [ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ] - } - }, - "exposure_modes": - { - "normal": - { - "shutter": [ 100, 15000, 30000, 60000, 120000 ], - "gain": [ 1.0, 1.0, 2.0, 4.0, 6.0 ] - }, - "short": - { - "shutter": [ 100, 5000, 10000, 20000, 120000 ], - "gain": [ 1.0, 2.0, 4.0, 6.0, 6.0 ] - } - }, - "constraint_modes": - { - "normal": [ - { - "bound": "LOWER", - "q_lo": 0.98, - "q_hi": 1.0, - "y_target": - [ - 0, 0.2, - 1000, 0.2 - ] - } - ], - "highlight": [ - { - "bound": "LOWER", - "q_lo": 0.98, - "q_hi": 1.0, - "y_target": - [ - 0, 0.2, - 1000, 0.2 - ] - }, - { - "bound": "UPPER", - "q_lo": 0.98, - "q_hi": 1.0, - "y_target": - [ - 0, 0.8, - 1000, 0.8 - ] - } - ] - }, - "y_target": - [ - 0, 0.16, - 1000, 0.165, - 10000, 0.17 - ], - "startup_frames": 5, - "convergence_frames": 6, - "speed": 0.15 - } - }, - { - "rpi.alsc": - { - "omega": 1.3, - "n_iter": 100, - "luminance_strength": 0.5, - "calibrations_Cr": [ - { - "ct": 3000, - "table": - [ - 1.562, 1.566, 1.566, 1.556, 1.533, 1.506, 1.475, 1.475, 1.475, 1.475, 1.506, 1.533, 1.555, 1.563, 1.562, 1.555, - 1.563, 1.564, 1.561, 1.538, 1.508, 1.482, 1.449, 1.436, 1.436, 1.449, 1.481, 1.508, 1.537, 1.557, 1.558, 1.557, - 1.564, 1.563, 1.554, 1.522, 1.482, 1.449, 1.421, 1.403, 1.403, 1.419, 1.449, 1.481, 1.519, 1.549, 1.557, 1.559, - 1.564, 1.563, 1.545, 1.506, 1.462, 1.421, 1.403, 1.378, 1.378, 1.402, 1.419, 1.459, 1.503, 1.541, 1.557, 1.559, - 1.564, 1.562, 1.537, 1.494, 1.447, 1.404, 1.378, 1.364, 1.364, 1.377, 1.402, 1.444, 1.491, 1.532, 1.556, 1.559, - 1.564, 1.559, 1.532, 1.487, 1.438, 1.395, 1.365, 1.359, 1.359, 1.364, 1.393, 1.436, 1.484, 1.527, 1.555, 1.558, - 1.564, 1.559, 1.532, 1.487, 1.438, 1.395, 1.365, 1.356, 1.356, 1.364, 1.393, 1.436, 1.484, 1.527, 1.554, 1.557, - 1.564, 1.561, 1.536, 1.492, 1.444, 1.402, 1.374, 1.364, 1.363, 1.373, 1.401, 1.442, 1.489, 1.531, 1.554, 1.557, - 1.564, 1.563, 1.544, 1.504, 1.458, 1.418, 1.397, 1.374, 1.374, 1.395, 1.416, 1.456, 1.501, 1.538, 1.556, 1.557, - 1.564, 1.562, 1.551, 1.518, 1.477, 1.441, 1.418, 1.397, 1.397, 1.416, 1.438, 1.474, 1.514, 1.546, 1.556, 1.556, - 1.562, 1.562, 1.558, 1.534, 1.499, 1.476, 1.441, 1.426, 1.426, 1.438, 1.473, 1.496, 1.531, 1.552, 1.556, 1.555, - 1.561, 1.564, 1.564, 1.552, 1.525, 1.497, 1.466, 1.461, 1.461, 1.464, 1.495, 1.523, 1.548, 1.556, 1.556, 1.552 - ] - }, - { - "ct": 5000, - "table": - [ - 2.609, 2.616, 2.617, 2.607, 2.573, 2.527, 2.483, 2.481, 2.481, 2.483, 2.529, 2.573, 2.604, 2.613, 2.613, 2.604, - 2.609, 2.615, 2.608, 2.576, 2.533, 2.489, 2.439, 2.418, 2.418, 2.439, 2.491, 2.532, 2.577, 2.605, 2.609, 2.607, - 2.611, 2.611, 2.597, 2.551, 2.489, 2.439, 2.391, 2.364, 2.364, 2.391, 2.439, 2.491, 2.551, 2.592, 2.607, 2.609, - 2.612, 2.608, 2.583, 2.526, 2.457, 2.391, 2.362, 2.318, 2.318, 2.362, 2.391, 2.458, 2.526, 2.581, 2.607, 2.611, - 2.612, 2.604, 2.571, 2.507, 2.435, 2.362, 2.317, 2.293, 2.294, 2.318, 2.363, 2.434, 2.508, 2.568, 2.604, 2.612, - 2.611, 2.602, 2.564, 2.496, 2.419, 2.349, 2.293, 2.284, 2.284, 2.294, 2.347, 2.421, 2.497, 2.562, 2.603, 2.611, - 2.609, 2.601, 2.564, 2.496, 2.419, 2.349, 2.293, 2.278, 2.278, 2.294, 2.347, 2.421, 2.497, 2.562, 2.602, 2.609, - 2.609, 2.602, 2.568, 2.503, 2.429, 2.361, 2.311, 2.292, 2.292, 2.309, 2.357, 2.429, 2.504, 2.567, 2.602, 2.609, - 2.606, 2.604, 2.579, 2.519, 2.449, 2.384, 2.348, 2.311, 2.311, 2.346, 2.383, 2.449, 2.521, 2.577, 2.604, 2.608, - 2.604, 2.603, 2.586, 2.537, 2.474, 2.418, 2.384, 2.348, 2.348, 2.383, 2.417, 2.476, 2.538, 2.586, 2.601, 2.603, - 2.603, 2.605, 2.596, 2.561, 2.508, 2.474, 2.418, 2.396, 2.396, 2.417, 2.474, 2.511, 2.562, 2.596, 2.603, 2.602, - 2.601, 2.607, 2.606, 2.589, 2.549, 2.507, 2.456, 2.454, 2.454, 2.458, 2.508, 2.554, 2.594, 2.605, 2.605, 2.602 - ] - } - ], - "calibrations_Cb": [ - { - "ct": 3000, - "table": - [ - 3.221, 3.226, 3.231, 3.236, 3.239, 3.243, 3.245, 3.247, 3.249, 3.253, 3.255, 3.254, 3.253, 3.242, 3.235, 3.226, - 3.225, 3.231, 3.235, 3.238, 3.241, 3.244, 3.246, 3.247, 3.249, 3.254, 3.256, 3.255, 3.252, 3.248, 3.241, 3.232, - 3.226, 3.234, 3.239, 3.243, 3.243, 3.245, 3.247, 3.248, 3.251, 3.255, 3.256, 3.256, 3.254, 3.249, 3.244, 3.236, - 3.232, 3.238, 3.245, 3.245, 3.246, 3.247, 3.248, 3.251, 3.251, 3.256, 3.257, 3.257, 3.256, 3.254, 3.249, 3.239, - 3.232, 3.243, 3.246, 3.246, 3.246, 3.247, 3.248, 3.251, 3.253, 3.257, 3.258, 3.258, 3.257, 3.256, 3.254, 3.239, - 3.232, 3.242, 3.246, 3.247, 3.246, 3.246, 3.248, 3.251, 3.252, 3.253, 3.256, 3.255, 3.255, 3.254, 3.251, 3.239, - 3.233, 3.241, 3.244, 3.245, 3.244, 3.245, 3.246, 3.249, 3.251, 3.252, 3.253, 3.252, 3.252, 3.252, 3.249, 3.238, - 3.238, 3.241, 3.246, 3.246, 3.245, 3.245, 3.247, 3.249, 3.251, 3.252, 3.253, 3.253, 3.252, 3.252, 3.249, 3.239, - 3.235, 3.241, 3.245, 3.245, 3.245, 3.245, 3.246, 3.247, 3.251, 3.254, 3.253, 3.255, 3.256, 3.255, 3.251, 3.241, - 3.226, 3.235, 3.241, 3.241, 3.241, 3.241, 3.243, 3.245, 3.246, 3.252, 3.253, 3.254, 3.256, 3.254, 3.241, 3.237, - 3.205, 3.213, 3.213, 3.214, 3.214, 3.214, 3.214, 3.213, 3.213, 3.216, 3.218, 3.216, 3.214, 3.213, 3.211, 3.208, - 3.205, 3.205, 3.212, 3.212, 3.212, 3.213, 3.211, 3.211, 3.211, 3.213, 3.216, 3.214, 3.213, 3.211, 3.208, 3.196 - ] - }, - { - "ct": 5000, - "table": - [ - 1.645, 1.646, 1.649, 1.653, 1.654, 1.657, 1.659, 1.661, 1.663, 1.662, 1.661, 1.659, 1.656, 1.651, 1.645, 1.642, - 1.646, 1.649, 1.652, 1.654, 1.656, 1.659, 1.662, 1.663, 1.664, 1.664, 1.662, 1.661, 1.657, 1.653, 1.649, 1.644, - 1.648, 1.652, 1.654, 1.656, 1.658, 1.662, 1.665, 1.668, 1.668, 1.668, 1.665, 1.662, 1.658, 1.655, 1.652, 1.646, - 1.649, 1.653, 1.656, 1.658, 1.661, 1.665, 1.667, 1.671, 1.673, 1.671, 1.668, 1.663, 1.659, 1.656, 1.654, 1.647, - 1.649, 1.655, 1.657, 1.659, 1.661, 1.666, 1.671, 1.674, 1.675, 1.673, 1.671, 1.664, 1.659, 1.656, 1.654, 1.648, - 1.649, 1.654, 1.656, 1.659, 1.661, 1.666, 1.673, 1.676, 1.676, 1.675, 1.671, 1.664, 1.659, 1.656, 1.654, 1.648, - 1.649, 1.654, 1.656, 1.658, 1.659, 1.665, 1.672, 1.675, 1.675, 1.674, 1.668, 1.662, 1.658, 1.655, 1.654, 1.646, - 1.652, 1.655, 1.657, 1.659, 1.661, 1.665, 1.671, 1.673, 1.673, 1.672, 1.668, 1.662, 1.658, 1.655, 1.654, 1.647, - 1.652, 1.655, 1.657, 1.659, 1.661, 1.664, 1.667, 1.671, 1.672, 1.668, 1.666, 1.662, 1.659, 1.656, 1.654, 1.647, - 1.647, 1.652, 1.655, 1.656, 1.657, 1.661, 1.664, 1.665, 1.665, 1.665, 1.663, 1.661, 1.657, 1.655, 1.647, 1.647, - 1.639, 1.642, 1.644, 1.645, 1.646, 1.648, 1.648, 1.648, 1.649, 1.649, 1.649, 1.646, 1.645, 1.642, 1.639, 1.636, - 1.639, 1.641, 1.642, 1.644, 1.645, 1.646, 1.647, 1.647, 1.648, 1.648, 1.647, 1.645, 1.642, 1.639, 1.636, 1.633 - ] - } - ], - "luminance_lut": - [ - 2.644, 2.396, 2.077, 1.863, 1.682, 1.535, 1.392, 1.382, 1.382, 1.382, 1.515, 1.657, 1.826, 2.035, 2.351, 2.604, - 2.497, 2.229, 1.947, 1.733, 1.539, 1.424, 1.296, 1.249, 1.249, 1.285, 1.401, 1.519, 1.699, 1.908, 2.183, 2.456, - 2.389, 2.109, 1.848, 1.622, 1.424, 1.296, 1.201, 1.146, 1.146, 1.188, 1.285, 1.401, 1.591, 1.811, 2.065, 2.347, - 2.317, 2.026, 1.771, 1.535, 1.339, 1.201, 1.145, 1.069, 1.069, 1.134, 1.188, 1.318, 1.505, 1.734, 1.983, 2.273, - 2.276, 1.972, 1.715, 1.474, 1.281, 1.148, 1.069, 1.033, 1.024, 1.065, 1.134, 1.262, 1.446, 1.679, 1.929, 2.233, - 2.268, 1.941, 1.682, 1.441, 1.251, 1.119, 1.033, 1.013, 1.013, 1.024, 1.105, 1.231, 1.415, 1.649, 1.898, 2.227, - 2.268, 1.941, 1.682, 1.441, 1.251, 1.119, 1.033, 1.001, 1.001, 1.024, 1.105, 1.231, 1.415, 1.649, 1.898, 2.227, - 2.268, 1.951, 1.694, 1.456, 1.265, 1.131, 1.044, 1.026, 1.019, 1.039, 1.118, 1.246, 1.429, 1.663, 1.912, 2.227, - 2.291, 1.992, 1.738, 1.505, 1.311, 1.175, 1.108, 1.044, 1.041, 1.106, 1.161, 1.292, 1.478, 1.707, 1.955, 2.252, - 2.347, 2.058, 1.803, 1.581, 1.384, 1.245, 1.175, 1.108, 1.108, 1.161, 1.239, 1.364, 1.551, 1.773, 2.023, 2.311, - 2.438, 2.156, 1.884, 1.674, 1.484, 1.373, 1.245, 1.199, 1.199, 1.239, 1.363, 1.463, 1.647, 1.858, 2.123, 2.406, - 2.563, 2.305, 1.998, 1.792, 1.615, 1.472, 1.339, 1.322, 1.322, 1.326, 1.456, 1.593, 1.767, 1.973, 2.273, 2.532 - ], - "sigma": 0.00178, - "sigma_Cb": 0.00217 - } - }, - { - "rpi.contrast": - { - "ce_enable": 1, - "gamma_curve": - [ - 0, 0, - 1024, 5040, - 2048, 9338, - 3072, 12356, - 4096, 15312, - 5120, 18051, - 6144, 20790, - 7168, 23193, - 8192, 25744, - 9216, 27942, - 10240, 30035, - 11264, 32005, - 12288, 33975, - 13312, 35815, - 14336, 37600, - 15360, 39168, - 16384, 40642, - 18432, 43379, - 20480, 45749, - 22528, 47753, - 24576, 49621, - 26624, 51253, - 28672, 52698, - 30720, 53796, - 32768, 54876, - 36864, 57012, - 40960, 58656, - 45056, 59954, - 49152, 61183, - 53248, 62355, - 57344, 63419, - 61440, 64476, - 65535, 65535 - ] - } - }, - { - "rpi.ccm": - { - "ccms": [ - { - "ct": 2498, - "ccm": - [ - 1.14912, 0.28638, -0.43551, - -0.49691, 1.60391, -0.10701, - -0.10513, -1.09534, 2.20047 - ] - }, - { - "ct": 2821, - "ccm": - [ - 1.18251, 0.15501, -0.33752, - -0.44304, 1.58495, -0.14191, - -0.05077, -0.96422, 2.01498 - ] - }, - { - "ct": 2925, - "ccm": - [ - 1.18668, 0.00195, -0.18864, - -0.41617, 1.50514, -0.08897, - -0.02675, -0.91143, 1.93818 - ] - }, - { - "ct": 2926, - "ccm": - [ - 1.50948, -0.44421, -0.06527, - -0.37241, 1.41726, -0.04486, - 0.07098, -0.84694, 1.77596 - ] - }, - { - "ct": 2951, - "ccm": - [ - 1.52743, -0.47333, -0.05411, - -0.36485, 1.40764, -0.04279, - 0.08672, -0.90479, 1.81807 - ] - }, - { - "ct": 2954, - "ccm": - [ - 1.51683, -0.46841, -0.04841, - -0.36288, 1.39914, -0.03625, - 0.06421, -0.82034, 1.75613 - ] - }, - { - "ct": 3578, - "ccm": - [ - 1.59888, -0.59105, -0.00784, - -0.29366, 1.32037, -0.02671, - 0.06627, -0.76465, 1.69838 - ] - }, - { - "ct": 3717, - "ccm": - [ - 1.59063, -0.58059, -0.01003, - -0.29583, 1.32715, -0.03132, - 0.03613, -0.67431, 1.63817 - ] - }, - { - "ct": 3784, - "ccm": - [ - 1.59379, -0.58861, -0.00517, - -0.29178, 1.33292, -0.04115, - 0.03541, -0.66162, 1.62622 - ] - }, - { - "ct": 4485, - "ccm": - [ - 1.40761, -0.34561, -0.06201, - -0.32388, 1.57221, -0.24832, - -0.01014, -0.63427, 1.64441 - ] - }, - { - "ct": 4615, - "ccm": - [ - 1.41537, -0.35832, -0.05705, - -0.31429, 1.56019, -0.24591, - -0.01761, -0.61859, 1.63621 - ] - }, - { - "ct": 4671, - "ccm": - [ - 1.42941, -0.38178, -0.04764, - -0.31421, 1.55925, -0.24504, - -0.01141, -0.62987, 1.64129 - ] - }, - { - "ct": 5753, - "ccm": - [ - 1.64549, -0.63329, -0.01221, - -0.22431, 1.36423, -0.13992, - -0.00831, -0.55373, 1.56204 - ] - }, - { - "ct": 5773, - "ccm": - [ - 1.63668, -0.63557, -0.00111, - -0.21919, 1.36234, -0.14315, - -0.00399, -0.57428, 1.57827 - ] - }, - { - "ct": 7433, - "ccm": - [ - 1.36007, -0.09277, -0.26729, - -0.36886, 2.09249, -0.72363, - -0.12573, -0.76761, 1.89334 - ] - }, - { - "ct": 55792, - "ccm": - [ - 1.65091, -0.63689, -0.01401, - -0.22277, 1.35752, -0.13475, - -0.00943, -0.55091, 1.56033 - ] - } - ] - } - }, - { - "rpi.sharpen": { } - }, - { - "rpi.af": - { - "ranges": - { - "normal": - { - "min": 0.0, - "max": 12.0, - "default": 1.0 - }, - "macro": - { - "min": 3.0, - "max": 15.0, - "default": 4.0 - } - }, - "speeds": - { - "normal": - { - "step_coarse": 1.0, - "step_fine": 0.25, - "contrast_ratio": 0.75, - "pdaf_gain": -0.02, - "pdaf_squelch": 0.125, - "max_slew": 2.0, - "pdaf_frames": 20, - "dropout_frames": 6, - "step_frames": 4 - } - }, - "conf_epsilon": 8, - "conf_thresh": 16, - "conf_clip": 512, - "skip_frames": 5, - "map": [ 0.0, 445, 15.0, 925 ] - } - } - ] -} diff --git a/src/ipa/raspberrypi/data/imx708_wide.json b/src/ipa/raspberrypi/data/imx708_wide.json deleted file mode 100644 index b772efee..00000000 --- a/src/ipa/raspberrypi/data/imx708_wide.json +++ /dev/null @@ -1,459 +0,0 @@ -{ - "version": 2.0, - "target": "bcm2835", - "algorithms": [ - { - "rpi.black_level": - { - "black_level": 4096 - } - }, - { - "rpi.dpc": { } - }, - { - "rpi.lux": - { - "reference_shutter_speed": 9989, - "reference_gain": 1.23, - "reference_aperture": 1.0, - "reference_lux": 980, - "reference_Y": 8345 - } - }, - { - "rpi.noise": - { - "reference_constant": 16.0, - "reference_slope": 4.0 - } - }, - { - "rpi.geq": - { - "offset": 215, - "slope": 0.00287 - } - }, - { - "rpi.sdn": { } - }, - { - "rpi.awb": - { - "priors": [ - { - "lux": 0, - "prior": - [ - 2000, 1.0, - 3000, 0.0, - 13000, 0.0 - ] - }, - { - "lux": 800, - "prior": - [ - 2000, 0.0, - 6000, 2.0, - 13000, 2.0 - ] - }, - { - "lux": 1500, - "prior": - [ - 2000, 0.0, - 4000, 1.0, - 6000, 6.0, - 6500, 7.0, - 7000, 1.0, - 13000, 1.0 - ] - } - ], - "modes": - { - "auto": - { - "lo": 2500, - "hi": 8000 - }, - "incandescent": - { - "lo": 2500, - "hi": 3000 - }, - "tungsten": - { - "lo": 3000, - "hi": 3500 - }, - "fluorescent": - { - "lo": 4000, - "hi": 4700 - }, - "indoor": - { - "lo": 3000, - "hi": 5000 - }, - "daylight": - { - "lo": 5500, - "hi": 6500 - }, - "cloudy": - { - "lo": 7000, - "hi": 8600 - } - }, - "bayes": 1, - "ct_curve": - [ - 2750.0, 0.7881, 0.2849, - 2940.0, 0.7559, 0.3103, - 3650.0, 0.6291, 0.4206, - 4625.0, 0.5336, 0.5161, - 5715.0, 0.4668, 0.5898 - ], - "sensitivity_r": 1.05, - "sensitivity_b": 1.05, - "transverse_pos": 0.01165, - "transverse_neg": 0.01601 - } - }, - { - "rpi.agc": - { - "metering_modes": - { - "centre-weighted": - { - "weights": [ 3, 3, 3, 2, 2, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0 ] - }, - "spot": - { - "weights": [ 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ] - }, - "matrix": - { - "weights": [ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ] - } - }, - "exposure_modes": - { - "normal": - { - "shutter": [ 100, 15000, 30000, 60000, 120000 ], - "gain": [ 1.0, 1.0, 2.0, 4.0, 6.0 ] - }, - "short": - { - "shutter": [ 100, 5000, 10000, 20000, 120000 ], - "gain": [ 1.0, 2.0, 4.0, 6.0, 6.0 ] - } - }, - "constraint_modes": - { - "normal": [ - { - "bound": "LOWER", - "q_lo": 0.98, - "q_hi": 1.0, - "y_target": - [ - 0, 0.2, - 1000, 0.2 - ] - } - ], - "highlight": [ - { - "bound": "LOWER", - "q_lo": 0.98, - "q_hi": 1.0, - "y_target": - [ - 0, 0.2, - 1000, 0.2 - ] - }, - { - "bound": "UPPER", - "q_lo": 0.98, - "q_hi": 1.0, - "y_target": - [ - 0, 0.8, - 1000, 0.8 - ] - } - ] - }, - "y_target": - [ - 0, 0.16, - 1000, 0.165, - 10000, 0.17 - ], - "startup_frames": 5, - "convergence_frames": 6, - "speed": 0.15 - } - }, - { - "rpi.alsc": - { - "omega": 1.3, - "n_iter": 100, - "luminance_strength": 0.5, - "calibrations_Cr": [ - { - "ct": 3000, - "table": - [ - 1.529, 1.526, 1.522, 1.506, 1.489, 1.473, 1.458, 1.456, 1.456, 1.458, 1.474, 1.493, 1.513, 1.531, 1.541, 1.544, - 1.527, 1.523, 1.511, 1.491, 1.474, 1.459, 1.445, 1.441, 1.441, 1.446, 1.461, 1.479, 1.499, 1.521, 1.536, 1.541, - 1.524, 1.515, 1.498, 1.477, 1.459, 1.444, 1.431, 1.426, 1.426, 1.435, 1.446, 1.466, 1.487, 1.507, 1.528, 1.538, - 1.522, 1.512, 1.491, 1.468, 1.447, 1.431, 1.423, 1.417, 1.418, 1.425, 1.435, 1.455, 1.479, 1.499, 1.523, 1.537, - 1.522, 1.509, 1.485, 1.463, 1.441, 1.423, 1.416, 1.413, 1.415, 1.418, 1.429, 1.449, 1.473, 1.495, 1.521, 1.538, - 1.522, 1.508, 1.483, 1.461, 1.438, 1.421, 1.413, 1.412, 1.412, 1.415, 1.428, 1.447, 1.471, 1.493, 1.519, 1.538, - 1.522, 1.509, 1.484, 1.462, 1.439, 1.421, 1.414, 1.411, 1.412, 1.416, 1.428, 1.447, 1.471, 1.493, 1.519, 1.537, - 1.523, 1.511, 1.487, 1.465, 1.443, 1.424, 1.417, 1.413, 1.415, 1.419, 1.429, 1.451, 1.473, 1.494, 1.519, 1.536, - 1.524, 1.514, 1.493, 1.471, 1.451, 1.434, 1.424, 1.419, 1.419, 1.428, 1.437, 1.457, 1.477, 1.498, 1.521, 1.538, - 1.527, 1.521, 1.503, 1.481, 1.462, 1.449, 1.434, 1.429, 1.429, 1.437, 1.451, 1.469, 1.488, 1.508, 1.527, 1.539, - 1.529, 1.527, 1.515, 1.495, 1.477, 1.462, 1.449, 1.444, 1.444, 1.451, 1.467, 1.481, 1.499, 1.519, 1.535, 1.543, - 1.534, 1.531, 1.527, 1.512, 1.492, 1.476, 1.463, 1.461, 1.461, 1.464, 1.479, 1.495, 1.515, 1.533, 1.543, 1.546 - ] - }, - { - "ct": 5000, - "table": - [ - 2.603, 2.599, 2.591, 2.567, 2.539, 2.515, 2.489, 2.489, 2.489, 2.491, 2.516, 2.543, 2.574, 2.597, 2.614, 2.617, - 2.596, 2.591, 2.571, 2.542, 2.516, 2.489, 2.464, 2.458, 2.458, 2.469, 2.492, 2.518, 2.547, 2.576, 2.602, 2.614, - 2.591, 2.576, 2.546, 2.519, 2.489, 2.464, 2.437, 2.427, 2.427, 2.441, 2.467, 2.492, 2.525, 2.553, 2.586, 2.605, - 2.588, 2.568, 2.534, 2.503, 2.472, 2.437, 2.423, 2.409, 2.411, 2.425, 2.441, 2.475, 2.513, 2.541, 2.577, 2.602, - 2.588, 2.565, 2.527, 2.494, 2.461, 2.425, 2.409, 2.399, 2.403, 2.409, 2.431, 2.466, 2.503, 2.534, 2.571, 2.601, - 2.586, 2.561, 2.525, 2.491, 2.454, 2.418, 2.399, 2.396, 2.395, 2.402, 2.424, 2.461, 2.501, 2.531, 2.567, 2.599, - 2.583, 2.559, 2.525, 2.491, 2.454, 2.418, 2.398, 2.393, 2.393, 2.401, 2.423, 2.459, 2.498, 2.531, 2.566, 2.597, - 2.583, 2.559, 2.526, 2.494, 2.458, 2.421, 2.404, 2.397, 2.399, 2.404, 2.426, 2.461, 2.501, 2.531, 2.566, 2.596, - 2.583, 2.563, 2.531, 2.501, 2.469, 2.435, 2.419, 2.405, 2.404, 2.422, 2.435, 2.471, 2.505, 2.537, 2.572, 2.596, - 2.585, 2.571, 2.539, 2.516, 2.486, 2.458, 2.435, 2.424, 2.424, 2.435, 2.459, 2.489, 2.521, 2.546, 2.579, 2.601, - 2.589, 2.578, 2.557, 2.532, 2.506, 2.483, 2.458, 2.449, 2.449, 2.459, 2.485, 2.507, 2.535, 2.563, 2.591, 2.605, - 2.589, 2.586, 2.575, 2.551, 2.525, 2.503, 2.481, 2.476, 2.476, 2.481, 2.504, 2.526, 2.555, 2.583, 2.604, 2.611 - ] - } - ], - "calibrations_Cb": [ - { - "ct": 3000, - "table": - [ - 3.311, 3.339, 3.369, 3.374, 3.371, 3.363, 3.356, 3.353, 3.353, 3.353, 3.357, 3.362, 3.362, 3.356, 3.328, 3.311, - 3.321, 3.354, 3.374, 3.374, 3.368, 3.359, 3.352, 3.349, 3.347, 3.347, 3.349, 3.357, 3.361, 3.359, 3.343, 3.324, - 3.334, 3.368, 3.375, 3.374, 3.365, 3.356, 3.349, 3.347, 3.346, 3.346, 3.347, 3.349, 3.358, 3.361, 3.357, 3.336, - 3.346, 3.378, 3.378, 3.369, 3.363, 3.358, 3.351, 3.348, 3.347, 3.346, 3.347, 3.348, 3.354, 3.364, 3.363, 3.345, - 3.351, 3.381, 3.381, 3.368, 3.361, 3.357, 3.349, 3.347, 3.347, 3.345, 3.345, 3.347, 3.353, 3.364, 3.364, 3.347, - 3.353, 3.379, 3.379, 3.366, 3.359, 3.351, 3.348, 3.343, 3.342, 3.342, 3.343, 3.345, 3.351, 3.363, 3.363, 3.347, - 3.353, 3.376, 3.376, 3.363, 3.351, 3.347, 3.343, 3.338, 3.336, 3.338, 3.339, 3.343, 3.351, 3.361, 3.361, 3.347, - 3.351, 3.374, 3.374, 3.359, 3.351, 3.345, 3.338, 3.334, 3.333, 3.334, 3.336, 3.339, 3.347, 3.358, 3.358, 3.345, - 3.346, 3.368, 3.368, 3.359, 3.349, 3.343, 3.336, 3.332, 3.327, 3.331, 3.333, 3.337, 3.346, 3.356, 3.356, 3.341, - 3.336, 3.362, 3.364, 3.359, 3.351, 3.342, 3.334, 3.324, 3.324, 3.325, 3.329, 3.336, 3.346, 3.351, 3.351, 3.333, - 3.324, 3.349, 3.359, 3.358, 3.352, 3.341, 3.329, 3.323, 3.321, 3.322, 3.326, 3.336, 3.346, 3.347, 3.339, 3.319, - 3.311, 3.328, 3.352, 3.354, 3.352, 3.341, 3.329, 3.321, 3.319, 3.321, 3.324, 3.338, 3.343, 3.343, 3.319, 3.312 - ] - }, - { - "ct": 5000, - "table": - [ - 1.634, 1.647, 1.665, 1.668, 1.668, 1.664, 1.662, 1.662, 1.661, 1.661, 1.661, 1.663, 1.663, 1.659, 1.643, 1.636, - 1.639, 1.656, 1.668, 1.669, 1.668, 1.666, 1.664, 1.663, 1.663, 1.661, 1.661, 1.662, 1.663, 1.662, 1.654, 1.642, - 1.645, 1.663, 1.669, 1.668, 1.667, 1.667, 1.667, 1.668, 1.668, 1.665, 1.662, 1.661, 1.662, 1.664, 1.661, 1.649, - 1.651, 1.669, 1.669, 1.667, 1.666, 1.668, 1.669, 1.672, 1.672, 1.668, 1.665, 1.661, 1.661, 1.665, 1.665, 1.655, - 1.654, 1.669, 1.669, 1.666, 1.666, 1.669, 1.672, 1.673, 1.673, 1.671, 1.666, 1.661, 1.661, 1.665, 1.665, 1.659, - 1.654, 1.669, 1.669, 1.666, 1.666, 1.669, 1.671, 1.673, 1.672, 1.669, 1.667, 1.661, 1.661, 1.665, 1.665, 1.659, - 1.654, 1.668, 1.668, 1.664, 1.663, 1.667, 1.669, 1.671, 1.669, 1.668, 1.665, 1.661, 1.661, 1.663, 1.663, 1.659, - 1.653, 1.665, 1.665, 1.661, 1.661, 1.664, 1.667, 1.668, 1.668, 1.665, 1.661, 1.658, 1.659, 1.662, 1.662, 1.657, - 1.651, 1.664, 1.664, 1.659, 1.659, 1.661, 1.663, 1.663, 1.662, 1.661, 1.658, 1.656, 1.657, 1.662, 1.662, 1.655, - 1.645, 1.661, 1.663, 1.661, 1.659, 1.659, 1.659, 1.657, 1.657, 1.656, 1.654, 1.655, 1.656, 1.661, 1.661, 1.649, - 1.641, 1.654, 1.661, 1.661, 1.659, 1.657, 1.655, 1.653, 1.652, 1.651, 1.652, 1.653, 1.657, 1.658, 1.655, 1.644, - 1.635, 1.645, 1.661, 1.661, 1.661, 1.655, 1.653, 1.649, 1.648, 1.647, 1.651, 1.653, 1.657, 1.657, 1.646, 1.638 - ] - } - ], - "luminance_lut": - [ - 3.535, 3.279, 3.049, 2.722, 2.305, 1.958, 1.657, 1.647, 1.647, 1.656, 1.953, 2.289, 2.707, 3.058, 3.325, 3.589, - 3.379, 3.157, 2.874, 2.421, 1.973, 1.735, 1.472, 1.388, 1.388, 1.471, 1.724, 1.963, 2.409, 2.877, 3.185, 3.416, - 3.288, 3.075, 2.696, 2.169, 1.735, 1.472, 1.311, 1.208, 1.208, 1.306, 1.471, 1.724, 2.159, 2.695, 3.092, 3.321, - 3.238, 3.001, 2.534, 1.981, 1.572, 1.311, 1.207, 1.082, 1.082, 1.204, 1.306, 1.563, 1.973, 2.529, 3.008, 3.259, - 3.211, 2.938, 2.414, 1.859, 1.468, 1.221, 1.082, 1.036, 1.031, 1.079, 1.217, 1.463, 1.851, 2.403, 2.931, 3.229, - 3.206, 2.904, 2.356, 1.802, 1.421, 1.181, 1.037, 1.002, 1.002, 1.032, 1.175, 1.414, 1.793, 2.343, 2.899, 3.223, - 3.206, 2.904, 2.356, 1.802, 1.421, 1.181, 1.037, 1.005, 1.005, 1.032, 1.175, 1.414, 1.793, 2.343, 2.899, 3.223, - 3.211, 2.936, 2.417, 1.858, 1.468, 1.222, 1.083, 1.037, 1.032, 1.083, 1.218, 1.463, 1.848, 2.403, 2.932, 3.226, - 3.234, 2.997, 2.536, 1.979, 1.569, 1.311, 1.206, 1.084, 1.084, 1.204, 1.305, 1.565, 1.966, 2.524, 2.996, 3.251, - 3.282, 3.069, 2.697, 2.166, 1.731, 1.471, 1.311, 1.207, 1.207, 1.305, 1.466, 1.729, 2.158, 2.689, 3.077, 3.304, - 3.369, 3.146, 2.873, 2.415, 1.964, 1.722, 1.471, 1.382, 1.382, 1.466, 1.722, 1.964, 2.408, 2.871, 3.167, 3.401, - 3.524, 3.253, 3.025, 2.691, 2.275, 1.939, 1.657, 1.628, 1.628, 1.654, 1.936, 2.275, 2.687, 3.029, 3.284, 3.574 - ], - "sigma": 0.00195, - "sigma_Cb": 0.00241 - } - }, - { - "rpi.contrast": - { - "ce_enable": 1, - "gamma_curve": - [ - 0, 0, - 1024, 5040, - 2048, 9338, - 3072, 12356, - 4096, 15312, - 5120, 18051, - 6144, 20790, - 7168, 23193, - 8192, 25744, - 9216, 27942, - 10240, 30035, - 11264, 32005, - 12288, 33975, - 13312, 35815, - 14336, 37600, - 15360, 39168, - 16384, 40642, - 18432, 43379, - 20480, 45749, - 22528, 47753, - 24576, 49621, - 26624, 51253, - 28672, 52698, - 30720, 53796, - 32768, 54876, - 36864, 57012, - 40960, 58656, - 45056, 59954, - 49152, 61183, - 53248, 62355, - 57344, 63419, - 61440, 64476, - 65535, 65535 - ] - } - }, - { - "rpi.ccm": - { - "ccms": [ - { - "ct": 2750, - "ccm": - [ - 1.13004, 0.36392, -0.49396, - -0.45885, 1.68171, -0.22286, - -0.06473, -0.86962, 1.93435 - ] - }, - { - "ct": 2940, - "ccm": - [ - 1.29876, 0.09627, -0.39503, - -0.43085, 1.60258, -0.17172, - -0.02638, -0.92581, 1.95218 - ] - }, - { - "ct": 3650, - "ccm": - [ - 1.57729, -0.29734, -0.27995, - -0.42965, 1.66231, -0.23265, - -0.02183, -0.62331, 1.64514 - ] - }, - { - "ct": 4625, - "ccm": - [ - 1.52145, -0.22382, -0.29763, - -0.40445, 1.82186, -0.41742, - -0.05732, -0.56222, 1.61954 - ] - }, - { - "ct": 5715, - "ccm": - [ - 1.67851, -0.39193, -0.28658, - -0.37169, 1.72949, -0.35781, - -0.09556, -0.41951, 1.51508 - ] - } - ] - } - }, - { - "rpi.sharpen": { } - }, - { - "rpi.af": - { - "ranges": - { - "normal": - { - "min": 0.0, - "max": 12.0, - "default": 1.0 - }, - "macro": - { - "min": 4.0, - "max": 32.0, - "default": 6.0 - } - }, - "speeds": - { - "normal": - { - "step_coarse": 2.0, - "step_fine": 0.5, - "contrast_ratio": 0.75, - "pdaf_gain": -0.03, - "pdaf_squelch": 0.2, - "max_slew": 4.0, - "pdaf_frames": 20, - "dropout_frames": 6, - "step_frames": 4 - }, - "fast": - { - "step_coarse": 2.0, - "step_fine": 0.5, - "contrast_ratio": 0.75, - "pdaf_gain": -0.05, - "pdaf_squelch": 0.2, - "max_slew": 5.0, - "pdaf_frames": 16, - "dropout_frames": 6, - "step_frames": 4 - } - }, - "conf_epsilon": 8, - "conf_thresh": 12, - "conf_clip": 512, - "skip_frames": 5, - "map": [ 0.0, 420, 35.0, 920 ] - } - } - ] -} diff --git a/src/ipa/raspberrypi/data/imx708_wide_noir.json b/src/ipa/raspberrypi/data/imx708_wide_noir.json deleted file mode 100644 index c5f6b53d..00000000 --- a/src/ipa/raspberrypi/data/imx708_wide_noir.json +++ /dev/null @@ -1,459 +0,0 @@ -{ - "version": 2.0, - "target": "bcm2835", - "algorithms": [ - { - "rpi.black_level": - { - "black_level": 4096 - } - }, - { - "rpi.dpc": { } - }, - { - "rpi.lux": - { - "reference_shutter_speed": 9989, - "reference_gain": 1.23, - "reference_aperture": 1.0, - "reference_lux": 980, - "reference_Y": 8345 - } - }, - { - "rpi.noise": - { - "reference_constant": 16.0, - "reference_slope": 4.0 - } - }, - { - "rpi.geq": - { - "offset": 215, - "slope": 0.00287 - } - }, - { - "rpi.sdn": { } - }, - { - "rpi.awb": - { - "priors": [ - { - "lux": 0, - "prior": - [ - 2000, 1.0, - 3000, 0.0, - 13000, 0.0 - ] - }, - { - "lux": 800, - "prior": - [ - 2000, 0.0, - 6000, 2.0, - 13000, 2.0 - ] - }, - { - "lux": 1500, - "prior": - [ - 2000, 0.0, - 4000, 1.0, - 6000, 6.0, - 6500, 7.0, - 7000, 1.0, - 13000, 1.0 - ] - } - ], - "modes": - { - "auto": - { - "lo": 2500, - "hi": 8000 - }, - "incandescent": - { - "lo": 2500, - "hi": 3000 - }, - "tungsten": - { - "lo": 3000, - "hi": 3500 - }, - "fluorescent": - { - "lo": 4000, - "hi": 4700 - }, - "indoor": - { - "lo": 3000, - "hi": 5000 - }, - "daylight": - { - "lo": 5500, - "hi": 6500 - }, - "cloudy": - { - "lo": 7000, - "hi": 8600 - } - }, - "bayes": 0, - "ct_curve": - [ - 2750.0, 0.7881, 0.2849, - 2940.0, 0.7559, 0.3103, - 3650.0, 0.6291, 0.4206, - 4625.0, 0.5336, 0.5161, - 5715.0, 0.4668, 0.5898 - ], - "sensitivity_r": 1.05, - "sensitivity_b": 1.05, - "transverse_pos": 0.01165, - "transverse_neg": 0.01601 - } - }, - { - "rpi.agc": - { - "metering_modes": - { - "centre-weighted": - { - "weights": [ 3, 3, 3, 2, 2, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0 ] - }, - "spot": - { - "weights": [ 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ] - }, - "matrix": - { - "weights": [ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ] - } - }, - "exposure_modes": - { - "normal": - { - "shutter": [ 100, 15000, 30000, 60000, 120000 ], - "gain": [ 1.0, 1.0, 2.0, 4.0, 6.0 ] - }, - "short": - { - "shutter": [ 100, 5000, 10000, 20000, 120000 ], - "gain": [ 1.0, 2.0, 4.0, 6.0, 6.0 ] - } - }, - "constraint_modes": - { - "normal": [ - { - "bound": "LOWER", - "q_lo": 0.98, - "q_hi": 1.0, - "y_target": - [ - 0, 0.2, - 1000, 0.2 - ] - } - ], - "highlight": [ - { - "bound": "LOWER", - "q_lo": 0.98, - "q_hi": 1.0, - "y_target": - [ - 0, 0.2, - 1000, 0.2 - ] - }, - { - "bound": "UPPER", - "q_lo": 0.98, - "q_hi": 1.0, - "y_target": - [ - 0, 0.8, - 1000, 0.8 - ] - } - ] - }, - "y_target": - [ - 0, 0.16, - 1000, 0.165, - 10000, 0.17 - ], - "startup_frames": 5, - "convergence_frames": 6, - "speed": 0.15 - } - }, - { - "rpi.alsc": - { - "omega": 1.3, - "n_iter": 100, - "luminance_strength": 0.5, - "calibrations_Cr": [ - { - "ct": 3000, - "table": - [ - 1.529, 1.526, 1.522, 1.506, 1.489, 1.473, 1.458, 1.456, 1.456, 1.458, 1.474, 1.493, 1.513, 1.531, 1.541, 1.544, - 1.527, 1.523, 1.511, 1.491, 1.474, 1.459, 1.445, 1.441, 1.441, 1.446, 1.461, 1.479, 1.499, 1.521, 1.536, 1.541, - 1.524, 1.515, 1.498, 1.477, 1.459, 1.444, 1.431, 1.426, 1.426, 1.435, 1.446, 1.466, 1.487, 1.507, 1.528, 1.538, - 1.522, 1.512, 1.491, 1.468, 1.447, 1.431, 1.423, 1.417, 1.418, 1.425, 1.435, 1.455, 1.479, 1.499, 1.523, 1.537, - 1.522, 1.509, 1.485, 1.463, 1.441, 1.423, 1.416, 1.413, 1.415, 1.418, 1.429, 1.449, 1.473, 1.495, 1.521, 1.538, - 1.522, 1.508, 1.483, 1.461, 1.438, 1.421, 1.413, 1.412, 1.412, 1.415, 1.428, 1.447, 1.471, 1.493, 1.519, 1.538, - 1.522, 1.509, 1.484, 1.462, 1.439, 1.421, 1.414, 1.411, 1.412, 1.416, 1.428, 1.447, 1.471, 1.493, 1.519, 1.537, - 1.523, 1.511, 1.487, 1.465, 1.443, 1.424, 1.417, 1.413, 1.415, 1.419, 1.429, 1.451, 1.473, 1.494, 1.519, 1.536, - 1.524, 1.514, 1.493, 1.471, 1.451, 1.434, 1.424, 1.419, 1.419, 1.428, 1.437, 1.457, 1.477, 1.498, 1.521, 1.538, - 1.527, 1.521, 1.503, 1.481, 1.462, 1.449, 1.434, 1.429, 1.429, 1.437, 1.451, 1.469, 1.488, 1.508, 1.527, 1.539, - 1.529, 1.527, 1.515, 1.495, 1.477, 1.462, 1.449, 1.444, 1.444, 1.451, 1.467, 1.481, 1.499, 1.519, 1.535, 1.543, - 1.534, 1.531, 1.527, 1.512, 1.492, 1.476, 1.463, 1.461, 1.461, 1.464, 1.479, 1.495, 1.515, 1.533, 1.543, 1.546 - ] - }, - { - "ct": 5000, - "table": - [ - 2.603, 2.599, 2.591, 2.567, 2.539, 2.515, 2.489, 2.489, 2.489, 2.491, 2.516, 2.543, 2.574, 2.597, 2.614, 2.617, - 2.596, 2.591, 2.571, 2.542, 2.516, 2.489, 2.464, 2.458, 2.458, 2.469, 2.492, 2.518, 2.547, 2.576, 2.602, 2.614, - 2.591, 2.576, 2.546, 2.519, 2.489, 2.464, 2.437, 2.427, 2.427, 2.441, 2.467, 2.492, 2.525, 2.553, 2.586, 2.605, - 2.588, 2.568, 2.534, 2.503, 2.472, 2.437, 2.423, 2.409, 2.411, 2.425, 2.441, 2.475, 2.513, 2.541, 2.577, 2.602, - 2.588, 2.565, 2.527, 2.494, 2.461, 2.425, 2.409, 2.399, 2.403, 2.409, 2.431, 2.466, 2.503, 2.534, 2.571, 2.601, - 2.586, 2.561, 2.525, 2.491, 2.454, 2.418, 2.399, 2.396, 2.395, 2.402, 2.424, 2.461, 2.501, 2.531, 2.567, 2.599, - 2.583, 2.559, 2.525, 2.491, 2.454, 2.418, 2.398, 2.393, 2.393, 2.401, 2.423, 2.459, 2.498, 2.531, 2.566, 2.597, - 2.583, 2.559, 2.526, 2.494, 2.458, 2.421, 2.404, 2.397, 2.399, 2.404, 2.426, 2.461, 2.501, 2.531, 2.566, 2.596, - 2.583, 2.563, 2.531, 2.501, 2.469, 2.435, 2.419, 2.405, 2.404, 2.422, 2.435, 2.471, 2.505, 2.537, 2.572, 2.596, - 2.585, 2.571, 2.539, 2.516, 2.486, 2.458, 2.435, 2.424, 2.424, 2.435, 2.459, 2.489, 2.521, 2.546, 2.579, 2.601, - 2.589, 2.578, 2.557, 2.532, 2.506, 2.483, 2.458, 2.449, 2.449, 2.459, 2.485, 2.507, 2.535, 2.563, 2.591, 2.605, - 2.589, 2.586, 2.575, 2.551, 2.525, 2.503, 2.481, 2.476, 2.476, 2.481, 2.504, 2.526, 2.555, 2.583, 2.604, 2.611 - ] - } - ], - "calibrations_Cb": [ - { - "ct": 3000, - "table": - [ - 3.311, 3.339, 3.369, 3.374, 3.371, 3.363, 3.356, 3.353, 3.353, 3.353, 3.357, 3.362, 3.362, 3.356, 3.328, 3.311, - 3.321, 3.354, 3.374, 3.374, 3.368, 3.359, 3.352, 3.349, 3.347, 3.347, 3.349, 3.357, 3.361, 3.359, 3.343, 3.324, - 3.334, 3.368, 3.375, 3.374, 3.365, 3.356, 3.349, 3.347, 3.346, 3.346, 3.347, 3.349, 3.358, 3.361, 3.357, 3.336, - 3.346, 3.378, 3.378, 3.369, 3.363, 3.358, 3.351, 3.348, 3.347, 3.346, 3.347, 3.348, 3.354, 3.364, 3.363, 3.345, - 3.351, 3.381, 3.381, 3.368, 3.361, 3.357, 3.349, 3.347, 3.347, 3.345, 3.345, 3.347, 3.353, 3.364, 3.364, 3.347, - 3.353, 3.379, 3.379, 3.366, 3.359, 3.351, 3.348, 3.343, 3.342, 3.342, 3.343, 3.345, 3.351, 3.363, 3.363, 3.347, - 3.353, 3.376, 3.376, 3.363, 3.351, 3.347, 3.343, 3.338, 3.336, 3.338, 3.339, 3.343, 3.351, 3.361, 3.361, 3.347, - 3.351, 3.374, 3.374, 3.359, 3.351, 3.345, 3.338, 3.334, 3.333, 3.334, 3.336, 3.339, 3.347, 3.358, 3.358, 3.345, - 3.346, 3.368, 3.368, 3.359, 3.349, 3.343, 3.336, 3.332, 3.327, 3.331, 3.333, 3.337, 3.346, 3.356, 3.356, 3.341, - 3.336, 3.362, 3.364, 3.359, 3.351, 3.342, 3.334, 3.324, 3.324, 3.325, 3.329, 3.336, 3.346, 3.351, 3.351, 3.333, - 3.324, 3.349, 3.359, 3.358, 3.352, 3.341, 3.329, 3.323, 3.321, 3.322, 3.326, 3.336, 3.346, 3.347, 3.339, 3.319, - 3.311, 3.328, 3.352, 3.354, 3.352, 3.341, 3.329, 3.321, 3.319, 3.321, 3.324, 3.338, 3.343, 3.343, 3.319, 3.312 - ] - }, - { - "ct": 5000, - "table": - [ - 1.634, 1.647, 1.665, 1.668, 1.668, 1.664, 1.662, 1.662, 1.661, 1.661, 1.661, 1.663, 1.663, 1.659, 1.643, 1.636, - 1.639, 1.656, 1.668, 1.669, 1.668, 1.666, 1.664, 1.663, 1.663, 1.661, 1.661, 1.662, 1.663, 1.662, 1.654, 1.642, - 1.645, 1.663, 1.669, 1.668, 1.667, 1.667, 1.667, 1.668, 1.668, 1.665, 1.662, 1.661, 1.662, 1.664, 1.661, 1.649, - 1.651, 1.669, 1.669, 1.667, 1.666, 1.668, 1.669, 1.672, 1.672, 1.668, 1.665, 1.661, 1.661, 1.665, 1.665, 1.655, - 1.654, 1.669, 1.669, 1.666, 1.666, 1.669, 1.672, 1.673, 1.673, 1.671, 1.666, 1.661, 1.661, 1.665, 1.665, 1.659, - 1.654, 1.669, 1.669, 1.666, 1.666, 1.669, 1.671, 1.673, 1.672, 1.669, 1.667, 1.661, 1.661, 1.665, 1.665, 1.659, - 1.654, 1.668, 1.668, 1.664, 1.663, 1.667, 1.669, 1.671, 1.669, 1.668, 1.665, 1.661, 1.661, 1.663, 1.663, 1.659, - 1.653, 1.665, 1.665, 1.661, 1.661, 1.664, 1.667, 1.668, 1.668, 1.665, 1.661, 1.658, 1.659, 1.662, 1.662, 1.657, - 1.651, 1.664, 1.664, 1.659, 1.659, 1.661, 1.663, 1.663, 1.662, 1.661, 1.658, 1.656, 1.657, 1.662, 1.662, 1.655, - 1.645, 1.661, 1.663, 1.661, 1.659, 1.659, 1.659, 1.657, 1.657, 1.656, 1.654, 1.655, 1.656, 1.661, 1.661, 1.649, - 1.641, 1.654, 1.661, 1.661, 1.659, 1.657, 1.655, 1.653, 1.652, 1.651, 1.652, 1.653, 1.657, 1.658, 1.655, 1.644, - 1.635, 1.645, 1.661, 1.661, 1.661, 1.655, 1.653, 1.649, 1.648, 1.647, 1.651, 1.653, 1.657, 1.657, 1.646, 1.638 - ] - } - ], - "luminance_lut": - [ - 3.535, 3.279, 3.049, 2.722, 2.305, 1.958, 1.657, 1.647, 1.647, 1.656, 1.953, 2.289, 2.707, 3.058, 3.325, 3.589, - 3.379, 3.157, 2.874, 2.421, 1.973, 1.735, 1.472, 1.388, 1.388, 1.471, 1.724, 1.963, 2.409, 2.877, 3.185, 3.416, - 3.288, 3.075, 2.696, 2.169, 1.735, 1.472, 1.311, 1.208, 1.208, 1.306, 1.471, 1.724, 2.159, 2.695, 3.092, 3.321, - 3.238, 3.001, 2.534, 1.981, 1.572, 1.311, 1.207, 1.082, 1.082, 1.204, 1.306, 1.563, 1.973, 2.529, 3.008, 3.259, - 3.211, 2.938, 2.414, 1.859, 1.468, 1.221, 1.082, 1.036, 1.031, 1.079, 1.217, 1.463, 1.851, 2.403, 2.931, 3.229, - 3.206, 2.904, 2.356, 1.802, 1.421, 1.181, 1.037, 1.002, 1.002, 1.032, 1.175, 1.414, 1.793, 2.343, 2.899, 3.223, - 3.206, 2.904, 2.356, 1.802, 1.421, 1.181, 1.037, 1.005, 1.005, 1.032, 1.175, 1.414, 1.793, 2.343, 2.899, 3.223, - 3.211, 2.936, 2.417, 1.858, 1.468, 1.222, 1.083, 1.037, 1.032, 1.083, 1.218, 1.463, 1.848, 2.403, 2.932, 3.226, - 3.234, 2.997, 2.536, 1.979, 1.569, 1.311, 1.206, 1.084, 1.084, 1.204, 1.305, 1.565, 1.966, 2.524, 2.996, 3.251, - 3.282, 3.069, 2.697, 2.166, 1.731, 1.471, 1.311, 1.207, 1.207, 1.305, 1.466, 1.729, 2.158, 2.689, 3.077, 3.304, - 3.369, 3.146, 2.873, 2.415, 1.964, 1.722, 1.471, 1.382, 1.382, 1.466, 1.722, 1.964, 2.408, 2.871, 3.167, 3.401, - 3.524, 3.253, 3.025, 2.691, 2.275, 1.939, 1.657, 1.628, 1.628, 1.654, 1.936, 2.275, 2.687, 3.029, 3.284, 3.574 - ], - "sigma": 0.00195, - "sigma_Cb": 0.00241 - } - }, - { - "rpi.contrast": - { - "ce_enable": 1, - "gamma_curve": - [ - 0, 0, - 1024, 5040, - 2048, 9338, - 3072, 12356, - 4096, 15312, - 5120, 18051, - 6144, 20790, - 7168, 23193, - 8192, 25744, - 9216, 27942, - 10240, 30035, - 11264, 32005, - 12288, 33975, - 13312, 35815, - 14336, 37600, - 15360, 39168, - 16384, 40642, - 18432, 43379, - 20480, 45749, - 22528, 47753, - 24576, 49621, - 26624, 51253, - 28672, 52698, - 30720, 53796, - 32768, 54876, - 36864, 57012, - 40960, 58656, - 45056, 59954, - 49152, 61183, - 53248, 62355, - 57344, 63419, - 61440, 64476, - 65535, 65535 - ] - } - }, - { - "rpi.ccm": - { - "ccms": [ - { - "ct": 2750, - "ccm": - [ - 1.13004, 0.36392, -0.49396, - -0.45885, 1.68171, -0.22286, - -0.06473, -0.86962, 1.93435 - ] - }, - { - "ct": 2940, - "ccm": - [ - 1.29876, 0.09627, -0.39503, - -0.43085, 1.60258, -0.17172, - -0.02638, -0.92581, 1.95218 - ] - }, - { - "ct": 3650, - "ccm": - [ - 1.57729, -0.29734, -0.27995, - -0.42965, 1.66231, -0.23265, - -0.02183, -0.62331, 1.64514 - ] - }, - { - "ct": 4625, - "ccm": - [ - 1.52145, -0.22382, -0.29763, - -0.40445, 1.82186, -0.41742, - -0.05732, -0.56222, 1.61954 - ] - }, - { - "ct": 5715, - "ccm": - [ - 1.67851, -0.39193, -0.28658, - -0.37169, 1.72949, -0.35781, - -0.09556, -0.41951, 1.51508 - ] - } - ] - } - }, - { - "rpi.sharpen": { } - }, - { - "rpi.af": - { - "ranges": - { - "normal": - { - "min": 0.0, - "max": 12.0, - "default": 1.0 - }, - "macro": - { - "min": 4.0, - "max": 32.0, - "default": 6.0 - } - }, - "speeds": - { - "normal": - { - "step_coarse": 2.0, - "step_fine": 0.5, - "contrast_ratio": 0.75, - "pdaf_gain": -0.03, - "pdaf_squelch": 0.2, - "max_slew": 4.0, - "pdaf_frames": 20, - "dropout_frames": 6, - "step_frames": 4 - }, - "fast": - { - "step_coarse": 2.0, - "step_fine": 0.5, - "contrast_ratio": 0.75, - "pdaf_gain": -0.05, - "pdaf_squelch": 0.2, - "max_slew": 5.0, - "pdaf_frames": 16, - "dropout_frames": 6, - "step_frames": 4 - } - }, - "conf_epsilon": 8, - "conf_thresh": 12, - "conf_clip": 512, - "skip_frames": 5, - "map": [ 0.0, 420, 35.0, 920 ] - } - } - ] -} diff --git a/src/ipa/raspberrypi/data/meson.build b/src/ipa/raspberrypi/data/meson.build deleted file mode 100644 index b163a052..00000000 --- a/src/ipa/raspberrypi/data/meson.build +++ /dev/null @@ -1,26 +0,0 @@ -# SPDX-License-Identifier: CC0-1.0 - -conf_files = files([ - 'imx219.json', - 'imx219_noir.json', - 'imx290.json', - 'imx296.json', - 'imx296_mono.json', - 'imx378.json', - 'imx477.json', - 'imx477_noir.json', - 'imx477_scientific.json', - 'imx519.json', - 'imx708.json', - 'imx708_noir.json', - 'imx708_wide.json', - 'imx708_wide_noir.json', - 'ov5647.json', - 'ov5647_noir.json', - 'ov9281_mono.json', - 'se327m12.json', - 'uncalibrated.json', -]) - -install_data(conf_files, - install_dir : ipa_data_dir / 'raspberrypi') diff --git a/src/ipa/raspberrypi/data/ov5647.json b/src/ipa/raspberrypi/data/ov5647.json deleted file mode 100644 index d770e44f..00000000 --- a/src/ipa/raspberrypi/data/ov5647.json +++ /dev/null @@ -1,487 +0,0 @@ -{ - "version": 2.0, - "target": "bcm2835", - "algorithms": [ - { - "rpi.black_level": - { - "black_level": 1024 - } - }, - { - "rpi.dpc": { } - }, - { - "rpi.lux": - { - "reference_shutter_speed": 21663, - "reference_gain": 1.0, - "reference_aperture": 1.0, - "reference_lux": 987, - "reference_Y": 8961 - } - }, - { - "rpi.noise": - { - "reference_constant": 0, - "reference_slope": 4.25 - } - }, - { - "rpi.geq": - { - "offset": 401, - "slope": 0.05619 - } - }, - { - "rpi.sdn": { } - }, - { - "rpi.awb": - { - "priors": [ - { - "lux": 0, - "prior": - [ - 2000, 1.0, - 3000, 0.0, - 13000, 0.0 - ] - }, - { - "lux": 800, - "prior": - [ - 2000, 0.0, - 6000, 2.0, - 13000, 2.0 - ] - }, - { - "lux": 1500, - "prior": - [ - 2000, 0.0, - 4000, 1.0, - 6000, 6.0, - 6500, 7.0, - 7000, 1.0, - 13000, 1.0 - ] - } - ], - "modes": - { - "auto": - { - "lo": 2500, - "hi": 8000 - }, - "incandescent": - { - "lo": 2500, - "hi": 3000 - }, - "tungsten": - { - "lo": 3000, - "hi": 3500 - }, - "fluorescent": - { - "lo": 4000, - "hi": 4700 - }, - "indoor": - { - "lo": 3000, - "hi": 5000 - }, - "daylight": - { - "lo": 5500, - "hi": 6500 - }, - "cloudy": - { - "lo": 7000, - "hi": 8600 - } - }, - "bayes": 1, - "ct_curve": - [ - 2500.0, 1.0289, 0.4503, - 2803.0, 0.9428, 0.5108, - 2914.0, 0.9406, 0.5127, - 3605.0, 0.8261, 0.6249, - 4540.0, 0.7331, 0.7533, - 5699.0, 0.6715, 0.8627, - 8625.0, 0.6081, 1.0012 - ], - "sensitivity_r": 1.05, - "sensitivity_b": 1.05, - "transverse_pos": 0.0321, - "transverse_neg": 0.04313 - } - }, - { - "rpi.agc": - { - "metering_modes": - { - "centre-weighted": - { - "weights": [ 3, 3, 3, 2, 2, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0 ] - }, - "spot": - { - "weights": [ 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ] - }, - "matrix": - { - "weights": [ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ] - } - }, - "exposure_modes": - { - "normal": - { - "shutter": [ 100, 10000, 30000, 60000, 66666 ], - "gain": [ 1.0, 2.0, 4.0, 6.0, 8.0 ] - }, - "short": - { - "shutter": [ 100, 5000, 10000, 20000, 33333 ], - "gain": [ 1.0, 2.0, 4.0, 6.0, 8.0 ] - }, - "long": - { - "shutter": [ 100, 10000, 30000, 60000, 120000 ], - "gain": [ 1.0, 2.0, 4.0, 6.0, 12.0 ] - } - }, - "constraint_modes": - { - "normal": [ - { - "bound": "LOWER", - "q_lo": 0.98, - "q_hi": 1.0, - "y_target": - [ - 0, 0.5, - 1000, 0.5 - ] - } - ], - "highlight": [ - { - "bound": "LOWER", - "q_lo": 0.98, - "q_hi": 1.0, - "y_target": - [ - 0, 0.5, - 1000, 0.5 - ] - }, - { - "bound": "UPPER", - "q_lo": 0.98, - "q_hi": 1.0, - "y_target": - [ - 0, 0.8, - 1000, 0.8 - ] - } - ], - "shadows": [ - { - "bound": "LOWER", - "q_lo": 0.0, - "q_hi": 0.5, - "y_target": - [ - 0, 0.17, - 1000, 0.17 - ] - } - ] - }, - "y_target": - [ - 0, 0.16, - 1000, 0.165, - 10000, 0.17 - ], - "base_ev": 1.25 - } - }, - { - "rpi.alsc": - { - "omega": 1.3, - "n_iter": 100, - "luminance_strength": 0.5, - "calibrations_Cr": [ - { - "ct": 3000, - "table": - [ - 1.105, 1.103, 1.093, 1.083, 1.071, 1.065, 1.065, 1.065, 1.066, 1.069, 1.072, 1.077, 1.084, 1.089, 1.093, 1.093, - 1.103, 1.096, 1.084, 1.072, 1.059, 1.051, 1.047, 1.047, 1.051, 1.053, 1.059, 1.067, 1.075, 1.082, 1.085, 1.086, - 1.096, 1.084, 1.072, 1.059, 1.051, 1.045, 1.039, 1.038, 1.039, 1.045, 1.049, 1.057, 1.063, 1.072, 1.081, 1.082, - 1.092, 1.075, 1.061, 1.052, 1.045, 1.039, 1.036, 1.035, 1.035, 1.039, 1.044, 1.049, 1.056, 1.063, 1.072, 1.081, - 1.092, 1.073, 1.058, 1.048, 1.043, 1.038, 1.035, 1.033, 1.033, 1.035, 1.039, 1.044, 1.051, 1.057, 1.069, 1.078, - 1.091, 1.068, 1.054, 1.045, 1.041, 1.038, 1.035, 1.032, 1.032, 1.032, 1.036, 1.041, 1.045, 1.055, 1.069, 1.078, - 1.091, 1.068, 1.052, 1.043, 1.041, 1.038, 1.035, 1.032, 1.031, 1.032, 1.034, 1.036, 1.043, 1.055, 1.069, 1.078, - 1.092, 1.068, 1.052, 1.047, 1.042, 1.041, 1.038, 1.035, 1.032, 1.032, 1.035, 1.039, 1.043, 1.055, 1.071, 1.079, - 1.092, 1.073, 1.057, 1.051, 1.047, 1.047, 1.044, 1.041, 1.038, 1.038, 1.039, 1.043, 1.051, 1.059, 1.076, 1.083, - 1.092, 1.081, 1.068, 1.058, 1.056, 1.056, 1.053, 1.052, 1.049, 1.048, 1.048, 1.051, 1.059, 1.066, 1.083, 1.085, - 1.091, 1.087, 1.081, 1.068, 1.065, 1.064, 1.062, 1.062, 1.061, 1.056, 1.056, 1.056, 1.064, 1.069, 1.084, 1.089, - 1.091, 1.089, 1.085, 1.079, 1.069, 1.068, 1.067, 1.067, 1.067, 1.063, 1.061, 1.063, 1.068, 1.069, 1.081, 1.092 - ] - }, - { - "ct": 5000, - "table": - [ - 1.486, 1.484, 1.468, 1.449, 1.427, 1.403, 1.399, 1.399, 1.399, 1.404, 1.413, 1.433, 1.454, 1.473, 1.482, 1.488, - 1.484, 1.472, 1.454, 1.431, 1.405, 1.381, 1.365, 1.365, 1.367, 1.373, 1.392, 1.411, 1.438, 1.458, 1.476, 1.481, - 1.476, 1.458, 1.433, 1.405, 1.381, 1.361, 1.339, 1.334, 1.334, 1.346, 1.362, 1.391, 1.411, 1.438, 1.462, 1.474, - 1.471, 1.443, 1.417, 1.388, 1.361, 1.339, 1.321, 1.313, 1.313, 1.327, 1.346, 1.362, 1.391, 1.422, 1.453, 1.473, - 1.469, 1.439, 1.408, 1.377, 1.349, 1.321, 1.312, 1.299, 1.299, 1.311, 1.327, 1.348, 1.378, 1.415, 1.446, 1.468, - 1.468, 1.434, 1.402, 1.371, 1.341, 1.316, 1.299, 1.296, 1.295, 1.299, 1.314, 1.338, 1.371, 1.408, 1.441, 1.466, - 1.468, 1.434, 1.401, 1.371, 1.341, 1.316, 1.301, 1.296, 1.295, 1.297, 1.314, 1.338, 1.369, 1.408, 1.441, 1.465, - 1.469, 1.436, 1.401, 1.374, 1.348, 1.332, 1.315, 1.301, 1.301, 1.313, 1.324, 1.342, 1.372, 1.409, 1.442, 1.465, - 1.471, 1.444, 1.413, 1.388, 1.371, 1.348, 1.332, 1.323, 1.323, 1.324, 1.342, 1.362, 1.386, 1.418, 1.449, 1.467, - 1.473, 1.454, 1.431, 1.407, 1.388, 1.371, 1.359, 1.352, 1.351, 1.351, 1.362, 1.383, 1.404, 1.433, 1.462, 1.472, - 1.474, 1.461, 1.447, 1.424, 1.407, 1.394, 1.385, 1.381, 1.379, 1.381, 1.383, 1.401, 1.419, 1.444, 1.466, 1.481, - 1.474, 1.464, 1.455, 1.442, 1.421, 1.408, 1.403, 1.403, 1.403, 1.399, 1.402, 1.415, 1.432, 1.446, 1.467, 1.483 - ] - }, - { - "ct": 6500, - "table": - [ - 1.567, 1.565, 1.555, 1.541, 1.525, 1.518, 1.518, 1.518, 1.521, 1.527, 1.532, 1.541, 1.551, 1.559, 1.567, 1.569, - 1.565, 1.557, 1.542, 1.527, 1.519, 1.515, 1.511, 1.516, 1.519, 1.524, 1.528, 1.533, 1.542, 1.553, 1.559, 1.562, - 1.561, 1.546, 1.532, 1.521, 1.518, 1.515, 1.511, 1.516, 1.519, 1.524, 1.528, 1.529, 1.533, 1.542, 1.554, 1.559, - 1.561, 1.539, 1.526, 1.524, 1.521, 1.521, 1.522, 1.524, 1.525, 1.531, 1.529, 1.529, 1.531, 1.538, 1.549, 1.558, - 1.559, 1.538, 1.526, 1.525, 1.524, 1.528, 1.534, 1.536, 1.536, 1.536, 1.532, 1.529, 1.531, 1.537, 1.548, 1.556, - 1.561, 1.537, 1.525, 1.524, 1.526, 1.532, 1.537, 1.539, 1.538, 1.537, 1.532, 1.529, 1.529, 1.537, 1.546, 1.556, - 1.561, 1.536, 1.524, 1.522, 1.525, 1.532, 1.538, 1.538, 1.537, 1.533, 1.528, 1.526, 1.527, 1.536, 1.546, 1.555, - 1.561, 1.537, 1.522, 1.521, 1.524, 1.531, 1.536, 1.537, 1.534, 1.529, 1.526, 1.522, 1.523, 1.534, 1.547, 1.555, - 1.561, 1.538, 1.524, 1.522, 1.526, 1.531, 1.535, 1.535, 1.534, 1.527, 1.524, 1.522, 1.522, 1.535, 1.549, 1.556, - 1.558, 1.543, 1.532, 1.526, 1.526, 1.529, 1.534, 1.535, 1.533, 1.526, 1.523, 1.522, 1.524, 1.537, 1.552, 1.557, - 1.555, 1.546, 1.541, 1.528, 1.527, 1.528, 1.531, 1.533, 1.531, 1.527, 1.522, 1.522, 1.526, 1.536, 1.552, 1.561, - 1.555, 1.547, 1.542, 1.538, 1.526, 1.526, 1.529, 1.531, 1.529, 1.528, 1.519, 1.519, 1.527, 1.531, 1.543, 1.561 - ] - } - ], - "calibrations_Cb": [ - { - "ct": 3000, - "table": - [ - 1.684, 1.688, 1.691, 1.697, 1.709, 1.722, 1.735, 1.745, 1.747, 1.745, 1.731, 1.719, 1.709, 1.705, 1.699, 1.699, - 1.684, 1.689, 1.694, 1.708, 1.721, 1.735, 1.747, 1.762, 1.762, 1.758, 1.745, 1.727, 1.716, 1.707, 1.701, 1.699, - 1.684, 1.691, 1.704, 1.719, 1.734, 1.755, 1.772, 1.786, 1.789, 1.788, 1.762, 1.745, 1.724, 1.709, 1.702, 1.698, - 1.682, 1.694, 1.709, 1.729, 1.755, 1.773, 1.798, 1.815, 1.817, 1.808, 1.788, 1.762, 1.733, 1.714, 1.704, 1.699, - 1.682, 1.693, 1.713, 1.742, 1.772, 1.798, 1.815, 1.829, 1.831, 1.821, 1.807, 1.773, 1.742, 1.716, 1.703, 1.699, - 1.681, 1.693, 1.713, 1.742, 1.772, 1.799, 1.828, 1.839, 1.839, 1.828, 1.807, 1.774, 1.742, 1.715, 1.699, 1.695, - 1.679, 1.691, 1.712, 1.739, 1.771, 1.798, 1.825, 1.829, 1.831, 1.818, 1.801, 1.774, 1.738, 1.712, 1.695, 1.691, - 1.676, 1.685, 1.703, 1.727, 1.761, 1.784, 1.801, 1.817, 1.817, 1.801, 1.779, 1.761, 1.729, 1.706, 1.691, 1.684, - 1.669, 1.678, 1.692, 1.714, 1.741, 1.764, 1.784, 1.795, 1.795, 1.779, 1.761, 1.738, 1.713, 1.696, 1.683, 1.679, - 1.664, 1.671, 1.679, 1.693, 1.716, 1.741, 1.762, 1.769, 1.769, 1.753, 1.738, 1.713, 1.701, 1.687, 1.681, 1.676, - 1.661, 1.664, 1.671, 1.679, 1.693, 1.714, 1.732, 1.739, 1.739, 1.729, 1.708, 1.701, 1.685, 1.679, 1.676, 1.677, - 1.659, 1.661, 1.664, 1.671, 1.679, 1.693, 1.712, 1.714, 1.714, 1.708, 1.701, 1.687, 1.679, 1.672, 1.673, 1.677 - ] - }, - { - "ct": 5000, - "table": - [ - 1.177, 1.183, 1.187, 1.191, 1.197, 1.206, 1.213, 1.215, 1.215, 1.215, 1.211, 1.204, 1.196, 1.191, 1.183, 1.182, - 1.179, 1.185, 1.191, 1.196, 1.206, 1.217, 1.224, 1.229, 1.229, 1.226, 1.221, 1.212, 1.202, 1.195, 1.188, 1.182, - 1.183, 1.191, 1.196, 1.206, 1.217, 1.229, 1.239, 1.245, 1.245, 1.245, 1.233, 1.221, 1.212, 1.199, 1.193, 1.187, - 1.183, 1.192, 1.201, 1.212, 1.229, 1.241, 1.252, 1.259, 1.259, 1.257, 1.245, 1.233, 1.217, 1.201, 1.194, 1.192, - 1.183, 1.192, 1.202, 1.219, 1.238, 1.252, 1.261, 1.269, 1.268, 1.261, 1.257, 1.241, 1.223, 1.204, 1.194, 1.191, - 1.182, 1.192, 1.202, 1.219, 1.239, 1.255, 1.266, 1.271, 1.271, 1.265, 1.258, 1.242, 1.223, 1.205, 1.192, 1.191, - 1.181, 1.189, 1.199, 1.218, 1.239, 1.254, 1.262, 1.268, 1.268, 1.258, 1.253, 1.241, 1.221, 1.204, 1.191, 1.187, - 1.179, 1.184, 1.193, 1.211, 1.232, 1.243, 1.254, 1.257, 1.256, 1.253, 1.242, 1.232, 1.216, 1.199, 1.187, 1.183, - 1.174, 1.179, 1.187, 1.202, 1.218, 1.232, 1.243, 1.246, 1.246, 1.239, 1.232, 1.218, 1.207, 1.191, 1.183, 1.179, - 1.169, 1.175, 1.181, 1.189, 1.202, 1.218, 1.229, 1.232, 1.232, 1.224, 1.218, 1.207, 1.199, 1.185, 1.181, 1.174, - 1.164, 1.168, 1.175, 1.179, 1.189, 1.201, 1.209, 1.213, 1.213, 1.209, 1.201, 1.198, 1.186, 1.181, 1.174, 1.173, - 1.161, 1.166, 1.171, 1.175, 1.179, 1.189, 1.197, 1.198, 1.198, 1.197, 1.196, 1.186, 1.182, 1.175, 1.173, 1.173 - ] - }, - { - "ct": 6500, - "table": - [ - 1.166, 1.171, 1.173, 1.178, 1.187, 1.193, 1.201, 1.205, 1.205, 1.205, 1.199, 1.191, 1.184, 1.179, 1.174, 1.171, - 1.166, 1.172, 1.176, 1.184, 1.195, 1.202, 1.209, 1.216, 1.216, 1.213, 1.208, 1.201, 1.189, 1.182, 1.176, 1.171, - 1.166, 1.173, 1.183, 1.195, 1.202, 1.214, 1.221, 1.228, 1.229, 1.228, 1.221, 1.209, 1.201, 1.186, 1.179, 1.174, - 1.165, 1.174, 1.187, 1.201, 1.214, 1.223, 1.235, 1.241, 1.242, 1.241, 1.229, 1.221, 1.205, 1.188, 1.181, 1.177, - 1.165, 1.174, 1.189, 1.207, 1.223, 1.235, 1.242, 1.253, 1.252, 1.245, 1.241, 1.228, 1.211, 1.189, 1.181, 1.178, - 1.164, 1.173, 1.189, 1.207, 1.224, 1.238, 1.249, 1.255, 1.255, 1.249, 1.242, 1.228, 1.211, 1.191, 1.179, 1.176, - 1.163, 1.172, 1.187, 1.207, 1.223, 1.237, 1.245, 1.253, 1.252, 1.243, 1.237, 1.228, 1.207, 1.188, 1.176, 1.173, - 1.159, 1.167, 1.179, 1.199, 1.217, 1.227, 1.237, 1.241, 1.241, 1.237, 1.228, 1.217, 1.201, 1.184, 1.174, 1.169, - 1.156, 1.164, 1.172, 1.189, 1.205, 1.217, 1.226, 1.229, 1.229, 1.222, 1.217, 1.204, 1.192, 1.177, 1.171, 1.166, - 1.154, 1.159, 1.166, 1.177, 1.189, 1.205, 1.213, 1.216, 1.216, 1.209, 1.204, 1.192, 1.183, 1.172, 1.168, 1.162, - 1.152, 1.155, 1.161, 1.166, 1.177, 1.188, 1.195, 1.198, 1.199, 1.196, 1.187, 1.183, 1.173, 1.168, 1.163, 1.162, - 1.151, 1.154, 1.158, 1.162, 1.168, 1.177, 1.183, 1.184, 1.184, 1.184, 1.182, 1.172, 1.168, 1.165, 1.162, 1.161 - ] - } - ], - "luminance_lut": - [ - 2.236, 2.111, 1.912, 1.741, 1.579, 1.451, 1.379, 1.349, 1.349, 1.361, 1.411, 1.505, 1.644, 1.816, 2.034, 2.159, - 2.139, 1.994, 1.796, 1.625, 1.467, 1.361, 1.285, 1.248, 1.239, 1.265, 1.321, 1.408, 1.536, 1.703, 1.903, 2.087, - 2.047, 1.898, 1.694, 1.511, 1.373, 1.254, 1.186, 1.152, 1.142, 1.166, 1.226, 1.309, 1.441, 1.598, 1.799, 1.978, - 1.999, 1.824, 1.615, 1.429, 1.281, 1.179, 1.113, 1.077, 1.071, 1.096, 1.153, 1.239, 1.357, 1.525, 1.726, 1.915, - 1.976, 1.773, 1.563, 1.374, 1.222, 1.119, 1.064, 1.032, 1.031, 1.049, 1.099, 1.188, 1.309, 1.478, 1.681, 1.893, - 1.973, 1.756, 1.542, 1.351, 1.196, 1.088, 1.028, 1.011, 1.004, 1.029, 1.077, 1.169, 1.295, 1.459, 1.663, 1.891, - 1.973, 1.761, 1.541, 1.349, 1.193, 1.087, 1.031, 1.006, 1.006, 1.023, 1.075, 1.169, 1.298, 1.463, 1.667, 1.891, - 1.982, 1.789, 1.568, 1.373, 1.213, 1.111, 1.051, 1.029, 1.024, 1.053, 1.106, 1.199, 1.329, 1.495, 1.692, 1.903, - 2.015, 1.838, 1.621, 1.426, 1.268, 1.159, 1.101, 1.066, 1.068, 1.099, 1.166, 1.259, 1.387, 1.553, 1.751, 1.937, - 2.076, 1.911, 1.692, 1.507, 1.346, 1.236, 1.169, 1.136, 1.139, 1.174, 1.242, 1.349, 1.475, 1.641, 1.833, 2.004, - 2.193, 2.011, 1.798, 1.604, 1.444, 1.339, 1.265, 1.235, 1.237, 1.273, 1.351, 1.461, 1.598, 1.758, 1.956, 2.125, - 2.263, 2.154, 1.916, 1.711, 1.549, 1.432, 1.372, 1.356, 1.356, 1.383, 1.455, 1.578, 1.726, 1.914, 2.119, 2.211 - ], - "sigma": 0.006, - "sigma_Cb": 0.00208 - } - }, - { - "rpi.contrast": - { - "ce_enable": 1, - "gamma_curve": - [ - 0, 0, - 1024, 5040, - 2048, 9338, - 3072, 12356, - 4096, 15312, - 5120, 18051, - 6144, 20790, - 7168, 23193, - 8192, 25744, - 9216, 27942, - 10240, 30035, - 11264, 32005, - 12288, 33975, - 13312, 35815, - 14336, 37600, - 15360, 39168, - 16384, 40642, - 18432, 43379, - 20480, 45749, - 22528, 47753, - 24576, 49621, - 26624, 51253, - 28672, 52698, - 30720, 53796, - 32768, 54876, - 36864, 57012, - 40960, 58656, - 45056, 59954, - 49152, 61183, - 53248, 62355, - 57344, 63419, - 61440, 64476, - 65535, 65535 - ] - } - }, - { - "rpi.ccm": - { - "ccms": [ - { - "ct": 2500, - "ccm": - [ - 1.70741, -0.05307, -0.65433, - -0.62822, 1.68836, -0.06014, - -0.04452, -1.87628, 2.92079 - ] - }, - { - "ct": 2803, - "ccm": - [ - 1.74383, -0.18731, -0.55652, - -0.56491, 1.67772, -0.11281, - -0.01522, -1.60635, 2.62157 - ] - }, - { - "ct": 2912, - "ccm": - [ - 1.75215, -0.22221, -0.52995, - -0.54568, 1.63522, -0.08954, - 0.02633, -1.56997, 2.54364 - ] - }, - { - "ct": 2914, - "ccm": - [ - 1.72423, -0.28939, -0.43484, - -0.55188, 1.62925, -0.07737, - 0.01959, -1.28661, 2.26702 - ] - }, - { - "ct": 3605, - "ccm": - [ - 1.80381, -0.43646, -0.36735, - -0.46505, 1.56814, -0.10309, - 0.00929, -1.00424, 1.99495 - ] - }, - { - "ct": 4540, - "ccm": - [ - 1.85263, -0.46545, -0.38719, - -0.44136, 1.68443, -0.24307, - 0.04108, -0.85599, 1.81491 - ] - }, - { - "ct": 5699, - "ccm": - [ - 1.98595, -0.63542, -0.35054, - -0.34623, 1.54146, -0.19522, - 0.00411, -0.70936, 1.70525 - ] - }, - { - "ct": 8625, - "ccm": - [ - 2.21637, -0.56663, -0.64974, - -0.41133, 1.96625, -0.55492, - -0.02307, -0.83529, 1.85837 - ] - } - ] - } - }, - { - "rpi.sharpen": { } - } - ] -} \ No newline at end of file diff --git a/src/ipa/raspberrypi/data/ov5647_noir.json b/src/ipa/raspberrypi/data/ov5647_noir.json deleted file mode 100644 index a6c6722f..00000000 --- a/src/ipa/raspberrypi/data/ov5647_noir.json +++ /dev/null @@ -1,403 +0,0 @@ -{ - "version": 2.0, - "target": "bcm2835", - "algorithms": [ - { - "rpi.black_level": - { - "black_level": 1024 - } - }, - { - "rpi.dpc": { } - }, - { - "rpi.lux": - { - "reference_shutter_speed": 21663, - "reference_gain": 1.0, - "reference_aperture": 1.0, - "reference_lux": 987, - "reference_Y": 8961 - } - }, - { - "rpi.noise": - { - "reference_constant": 0, - "reference_slope": 4.25 - } - }, - { - "rpi.geq": - { - "offset": 401, - "slope": 0.05619 - } - }, - { - "rpi.sdn": { } - }, - { - "rpi.awb": - { - "bayes": 0 - } - }, - { - "rpi.agc": - { - "metering_modes": - { - "centre-weighted": - { - "weights": [ 3, 3, 3, 2, 2, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0 ] - }, - "spot": - { - "weights": [ 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ] - }, - "matrix": - { - "weights": [ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ] - } - }, - "exposure_modes": - { - "normal": - { - "shutter": [ 100, 10000, 30000, 60000, 66666 ], - "gain": [ 1.0, 2.0, 4.0, 6.0, 8.0 ] - }, - "short": - { - "shutter": [ 100, 5000, 10000, 20000, 33333 ], - "gain": [ 1.0, 2.0, 4.0, 6.0, 8.0 ] - }, - "long": - { - "shutter": [ 100, 10000, 30000, 60000, 120000 ], - "gain": [ 1.0, 2.0, 4.0, 6.0, 12.0 ] - } - }, - "constraint_modes": - { - "normal": [ - { - "bound": "LOWER", - "q_lo": 0.98, - "q_hi": 1.0, - "y_target": - [ - 0, 0.5, - 1000, 0.5 - ] - } - ], - "highlight": [ - { - "bound": "LOWER", - "q_lo": 0.98, - "q_hi": 1.0, - "y_target": - [ - 0, 0.5, - 1000, 0.5 - ] - }, - { - "bound": "UPPER", - "q_lo": 0.98, - "q_hi": 1.0, - "y_target": - [ - 0, 0.8, - 1000, 0.8 - ] - } - ], - "shadows": [ - { - "bound": "LOWER", - "q_lo": 0.0, - "q_hi": 0.5, - "y_target": - [ - 0, 0.17, - 1000, 0.17 - ] - } - ] - }, - "y_target": - [ - 0, 0.16, - 1000, 0.165, - 10000, 0.17 - ], - "base_ev": 1.25 - } - }, - { - "rpi.alsc": - { - "omega": 1.3, - "n_iter": 100, - "luminance_strength": 0.5, - "calibrations_Cr": [ - { - "ct": 3000, - "table": - [ - 1.105, 1.103, 1.093, 1.083, 1.071, 1.065, 1.065, 1.065, 1.066, 1.069, 1.072, 1.077, 1.084, 1.089, 1.093, 1.093, - 1.103, 1.096, 1.084, 1.072, 1.059, 1.051, 1.047, 1.047, 1.051, 1.053, 1.059, 1.067, 1.075, 1.082, 1.085, 1.086, - 1.096, 1.084, 1.072, 1.059, 1.051, 1.045, 1.039, 1.038, 1.039, 1.045, 1.049, 1.057, 1.063, 1.072, 1.081, 1.082, - 1.092, 1.075, 1.061, 1.052, 1.045, 1.039, 1.036, 1.035, 1.035, 1.039, 1.044, 1.049, 1.056, 1.063, 1.072, 1.081, - 1.092, 1.073, 1.058, 1.048, 1.043, 1.038, 1.035, 1.033, 1.033, 1.035, 1.039, 1.044, 1.051, 1.057, 1.069, 1.078, - 1.091, 1.068, 1.054, 1.045, 1.041, 1.038, 1.035, 1.032, 1.032, 1.032, 1.036, 1.041, 1.045, 1.055, 1.069, 1.078, - 1.091, 1.068, 1.052, 1.043, 1.041, 1.038, 1.035, 1.032, 1.031, 1.032, 1.034, 1.036, 1.043, 1.055, 1.069, 1.078, - 1.092, 1.068, 1.052, 1.047, 1.042, 1.041, 1.038, 1.035, 1.032, 1.032, 1.035, 1.039, 1.043, 1.055, 1.071, 1.079, - 1.092, 1.073, 1.057, 1.051, 1.047, 1.047, 1.044, 1.041, 1.038, 1.038, 1.039, 1.043, 1.051, 1.059, 1.076, 1.083, - 1.092, 1.081, 1.068, 1.058, 1.056, 1.056, 1.053, 1.052, 1.049, 1.048, 1.048, 1.051, 1.059, 1.066, 1.083, 1.085, - 1.091, 1.087, 1.081, 1.068, 1.065, 1.064, 1.062, 1.062, 1.061, 1.056, 1.056, 1.056, 1.064, 1.069, 1.084, 1.089, - 1.091, 1.089, 1.085, 1.079, 1.069, 1.068, 1.067, 1.067, 1.067, 1.063, 1.061, 1.063, 1.068, 1.069, 1.081, 1.092 - ] - }, - { - "ct": 5000, - "table": - [ - 1.486, 1.484, 1.468, 1.449, 1.427, 1.403, 1.399, 1.399, 1.399, 1.404, 1.413, 1.433, 1.454, 1.473, 1.482, 1.488, - 1.484, 1.472, 1.454, 1.431, 1.405, 1.381, 1.365, 1.365, 1.367, 1.373, 1.392, 1.411, 1.438, 1.458, 1.476, 1.481, - 1.476, 1.458, 1.433, 1.405, 1.381, 1.361, 1.339, 1.334, 1.334, 1.346, 1.362, 1.391, 1.411, 1.438, 1.462, 1.474, - 1.471, 1.443, 1.417, 1.388, 1.361, 1.339, 1.321, 1.313, 1.313, 1.327, 1.346, 1.362, 1.391, 1.422, 1.453, 1.473, - 1.469, 1.439, 1.408, 1.377, 1.349, 1.321, 1.312, 1.299, 1.299, 1.311, 1.327, 1.348, 1.378, 1.415, 1.446, 1.468, - 1.468, 1.434, 1.402, 1.371, 1.341, 1.316, 1.299, 1.296, 1.295, 1.299, 1.314, 1.338, 1.371, 1.408, 1.441, 1.466, - 1.468, 1.434, 1.401, 1.371, 1.341, 1.316, 1.301, 1.296, 1.295, 1.297, 1.314, 1.338, 1.369, 1.408, 1.441, 1.465, - 1.469, 1.436, 1.401, 1.374, 1.348, 1.332, 1.315, 1.301, 1.301, 1.313, 1.324, 1.342, 1.372, 1.409, 1.442, 1.465, - 1.471, 1.444, 1.413, 1.388, 1.371, 1.348, 1.332, 1.323, 1.323, 1.324, 1.342, 1.362, 1.386, 1.418, 1.449, 1.467, - 1.473, 1.454, 1.431, 1.407, 1.388, 1.371, 1.359, 1.352, 1.351, 1.351, 1.362, 1.383, 1.404, 1.433, 1.462, 1.472, - 1.474, 1.461, 1.447, 1.424, 1.407, 1.394, 1.385, 1.381, 1.379, 1.381, 1.383, 1.401, 1.419, 1.444, 1.466, 1.481, - 1.474, 1.464, 1.455, 1.442, 1.421, 1.408, 1.403, 1.403, 1.403, 1.399, 1.402, 1.415, 1.432, 1.446, 1.467, 1.483 - ] - }, - { - "ct": 6500, - "table": - [ - 1.567, 1.565, 1.555, 1.541, 1.525, 1.518, 1.518, 1.518, 1.521, 1.527, 1.532, 1.541, 1.551, 1.559, 1.567, 1.569, - 1.565, 1.557, 1.542, 1.527, 1.519, 1.515, 1.511, 1.516, 1.519, 1.524, 1.528, 1.533, 1.542, 1.553, 1.559, 1.562, - 1.561, 1.546, 1.532, 1.521, 1.518, 1.515, 1.511, 1.516, 1.519, 1.524, 1.528, 1.529, 1.533, 1.542, 1.554, 1.559, - 1.561, 1.539, 1.526, 1.524, 1.521, 1.521, 1.522, 1.524, 1.525, 1.531, 1.529, 1.529, 1.531, 1.538, 1.549, 1.558, - 1.559, 1.538, 1.526, 1.525, 1.524, 1.528, 1.534, 1.536, 1.536, 1.536, 1.532, 1.529, 1.531, 1.537, 1.548, 1.556, - 1.561, 1.537, 1.525, 1.524, 1.526, 1.532, 1.537, 1.539, 1.538, 1.537, 1.532, 1.529, 1.529, 1.537, 1.546, 1.556, - 1.561, 1.536, 1.524, 1.522, 1.525, 1.532, 1.538, 1.538, 1.537, 1.533, 1.528, 1.526, 1.527, 1.536, 1.546, 1.555, - 1.561, 1.537, 1.522, 1.521, 1.524, 1.531, 1.536, 1.537, 1.534, 1.529, 1.526, 1.522, 1.523, 1.534, 1.547, 1.555, - 1.561, 1.538, 1.524, 1.522, 1.526, 1.531, 1.535, 1.535, 1.534, 1.527, 1.524, 1.522, 1.522, 1.535, 1.549, 1.556, - 1.558, 1.543, 1.532, 1.526, 1.526, 1.529, 1.534, 1.535, 1.533, 1.526, 1.523, 1.522, 1.524, 1.537, 1.552, 1.557, - 1.555, 1.546, 1.541, 1.528, 1.527, 1.528, 1.531, 1.533, 1.531, 1.527, 1.522, 1.522, 1.526, 1.536, 1.552, 1.561, - 1.555, 1.547, 1.542, 1.538, 1.526, 1.526, 1.529, 1.531, 1.529, 1.528, 1.519, 1.519, 1.527, 1.531, 1.543, 1.561 - ] - } - ], - "calibrations_Cb": [ - { - "ct": 3000, - "table": - [ - 1.684, 1.688, 1.691, 1.697, 1.709, 1.722, 1.735, 1.745, 1.747, 1.745, 1.731, 1.719, 1.709, 1.705, 1.699, 1.699, - 1.684, 1.689, 1.694, 1.708, 1.721, 1.735, 1.747, 1.762, 1.762, 1.758, 1.745, 1.727, 1.716, 1.707, 1.701, 1.699, - 1.684, 1.691, 1.704, 1.719, 1.734, 1.755, 1.772, 1.786, 1.789, 1.788, 1.762, 1.745, 1.724, 1.709, 1.702, 1.698, - 1.682, 1.694, 1.709, 1.729, 1.755, 1.773, 1.798, 1.815, 1.817, 1.808, 1.788, 1.762, 1.733, 1.714, 1.704, 1.699, - 1.682, 1.693, 1.713, 1.742, 1.772, 1.798, 1.815, 1.829, 1.831, 1.821, 1.807, 1.773, 1.742, 1.716, 1.703, 1.699, - 1.681, 1.693, 1.713, 1.742, 1.772, 1.799, 1.828, 1.839, 1.839, 1.828, 1.807, 1.774, 1.742, 1.715, 1.699, 1.695, - 1.679, 1.691, 1.712, 1.739, 1.771, 1.798, 1.825, 1.829, 1.831, 1.818, 1.801, 1.774, 1.738, 1.712, 1.695, 1.691, - 1.676, 1.685, 1.703, 1.727, 1.761, 1.784, 1.801, 1.817, 1.817, 1.801, 1.779, 1.761, 1.729, 1.706, 1.691, 1.684, - 1.669, 1.678, 1.692, 1.714, 1.741, 1.764, 1.784, 1.795, 1.795, 1.779, 1.761, 1.738, 1.713, 1.696, 1.683, 1.679, - 1.664, 1.671, 1.679, 1.693, 1.716, 1.741, 1.762, 1.769, 1.769, 1.753, 1.738, 1.713, 1.701, 1.687, 1.681, 1.676, - 1.661, 1.664, 1.671, 1.679, 1.693, 1.714, 1.732, 1.739, 1.739, 1.729, 1.708, 1.701, 1.685, 1.679, 1.676, 1.677, - 1.659, 1.661, 1.664, 1.671, 1.679, 1.693, 1.712, 1.714, 1.714, 1.708, 1.701, 1.687, 1.679, 1.672, 1.673, 1.677 - ] - }, - { - "ct": 5000, - "table": - [ - 1.177, 1.183, 1.187, 1.191, 1.197, 1.206, 1.213, 1.215, 1.215, 1.215, 1.211, 1.204, 1.196, 1.191, 1.183, 1.182, - 1.179, 1.185, 1.191, 1.196, 1.206, 1.217, 1.224, 1.229, 1.229, 1.226, 1.221, 1.212, 1.202, 1.195, 1.188, 1.182, - 1.183, 1.191, 1.196, 1.206, 1.217, 1.229, 1.239, 1.245, 1.245, 1.245, 1.233, 1.221, 1.212, 1.199, 1.193, 1.187, - 1.183, 1.192, 1.201, 1.212, 1.229, 1.241, 1.252, 1.259, 1.259, 1.257, 1.245, 1.233, 1.217, 1.201, 1.194, 1.192, - 1.183, 1.192, 1.202, 1.219, 1.238, 1.252, 1.261, 1.269, 1.268, 1.261, 1.257, 1.241, 1.223, 1.204, 1.194, 1.191, - 1.182, 1.192, 1.202, 1.219, 1.239, 1.255, 1.266, 1.271, 1.271, 1.265, 1.258, 1.242, 1.223, 1.205, 1.192, 1.191, - 1.181, 1.189, 1.199, 1.218, 1.239, 1.254, 1.262, 1.268, 1.268, 1.258, 1.253, 1.241, 1.221, 1.204, 1.191, 1.187, - 1.179, 1.184, 1.193, 1.211, 1.232, 1.243, 1.254, 1.257, 1.256, 1.253, 1.242, 1.232, 1.216, 1.199, 1.187, 1.183, - 1.174, 1.179, 1.187, 1.202, 1.218, 1.232, 1.243, 1.246, 1.246, 1.239, 1.232, 1.218, 1.207, 1.191, 1.183, 1.179, - 1.169, 1.175, 1.181, 1.189, 1.202, 1.218, 1.229, 1.232, 1.232, 1.224, 1.218, 1.207, 1.199, 1.185, 1.181, 1.174, - 1.164, 1.168, 1.175, 1.179, 1.189, 1.201, 1.209, 1.213, 1.213, 1.209, 1.201, 1.198, 1.186, 1.181, 1.174, 1.173, - 1.161, 1.166, 1.171, 1.175, 1.179, 1.189, 1.197, 1.198, 1.198, 1.197, 1.196, 1.186, 1.182, 1.175, 1.173, 1.173 - ] - }, - { - "ct": 6500, - "table": - [ - 1.166, 1.171, 1.173, 1.178, 1.187, 1.193, 1.201, 1.205, 1.205, 1.205, 1.199, 1.191, 1.184, 1.179, 1.174, 1.171, - 1.166, 1.172, 1.176, 1.184, 1.195, 1.202, 1.209, 1.216, 1.216, 1.213, 1.208, 1.201, 1.189, 1.182, 1.176, 1.171, - 1.166, 1.173, 1.183, 1.195, 1.202, 1.214, 1.221, 1.228, 1.229, 1.228, 1.221, 1.209, 1.201, 1.186, 1.179, 1.174, - 1.165, 1.174, 1.187, 1.201, 1.214, 1.223, 1.235, 1.241, 1.242, 1.241, 1.229, 1.221, 1.205, 1.188, 1.181, 1.177, - 1.165, 1.174, 1.189, 1.207, 1.223, 1.235, 1.242, 1.253, 1.252, 1.245, 1.241, 1.228, 1.211, 1.189, 1.181, 1.178, - 1.164, 1.173, 1.189, 1.207, 1.224, 1.238, 1.249, 1.255, 1.255, 1.249, 1.242, 1.228, 1.211, 1.191, 1.179, 1.176, - 1.163, 1.172, 1.187, 1.207, 1.223, 1.237, 1.245, 1.253, 1.252, 1.243, 1.237, 1.228, 1.207, 1.188, 1.176, 1.173, - 1.159, 1.167, 1.179, 1.199, 1.217, 1.227, 1.237, 1.241, 1.241, 1.237, 1.228, 1.217, 1.201, 1.184, 1.174, 1.169, - 1.156, 1.164, 1.172, 1.189, 1.205, 1.217, 1.226, 1.229, 1.229, 1.222, 1.217, 1.204, 1.192, 1.177, 1.171, 1.166, - 1.154, 1.159, 1.166, 1.177, 1.189, 1.205, 1.213, 1.216, 1.216, 1.209, 1.204, 1.192, 1.183, 1.172, 1.168, 1.162, - 1.152, 1.155, 1.161, 1.166, 1.177, 1.188, 1.195, 1.198, 1.199, 1.196, 1.187, 1.183, 1.173, 1.168, 1.163, 1.162, - 1.151, 1.154, 1.158, 1.162, 1.168, 1.177, 1.183, 1.184, 1.184, 1.184, 1.182, 1.172, 1.168, 1.165, 1.162, 1.161 - ] - } - ], - "luminance_lut": - [ - 2.236, 2.111, 1.912, 1.741, 1.579, 1.451, 1.379, 1.349, 1.349, 1.361, 1.411, 1.505, 1.644, 1.816, 2.034, 2.159, - 2.139, 1.994, 1.796, 1.625, 1.467, 1.361, 1.285, 1.248, 1.239, 1.265, 1.321, 1.408, 1.536, 1.703, 1.903, 2.087, - 2.047, 1.898, 1.694, 1.511, 1.373, 1.254, 1.186, 1.152, 1.142, 1.166, 1.226, 1.309, 1.441, 1.598, 1.799, 1.978, - 1.999, 1.824, 1.615, 1.429, 1.281, 1.179, 1.113, 1.077, 1.071, 1.096, 1.153, 1.239, 1.357, 1.525, 1.726, 1.915, - 1.976, 1.773, 1.563, 1.374, 1.222, 1.119, 1.064, 1.032, 1.031, 1.049, 1.099, 1.188, 1.309, 1.478, 1.681, 1.893, - 1.973, 1.756, 1.542, 1.351, 1.196, 1.088, 1.028, 1.011, 1.004, 1.029, 1.077, 1.169, 1.295, 1.459, 1.663, 1.891, - 1.973, 1.761, 1.541, 1.349, 1.193, 1.087, 1.031, 1.006, 1.006, 1.023, 1.075, 1.169, 1.298, 1.463, 1.667, 1.891, - 1.982, 1.789, 1.568, 1.373, 1.213, 1.111, 1.051, 1.029, 1.024, 1.053, 1.106, 1.199, 1.329, 1.495, 1.692, 1.903, - 2.015, 1.838, 1.621, 1.426, 1.268, 1.159, 1.101, 1.066, 1.068, 1.099, 1.166, 1.259, 1.387, 1.553, 1.751, 1.937, - 2.076, 1.911, 1.692, 1.507, 1.346, 1.236, 1.169, 1.136, 1.139, 1.174, 1.242, 1.349, 1.475, 1.641, 1.833, 2.004, - 2.193, 2.011, 1.798, 1.604, 1.444, 1.339, 1.265, 1.235, 1.237, 1.273, 1.351, 1.461, 1.598, 1.758, 1.956, 2.125, - 2.263, 2.154, 1.916, 1.711, 1.549, 1.432, 1.372, 1.356, 1.356, 1.383, 1.455, 1.578, 1.726, 1.914, 2.119, 2.211 - ], - "sigma": 0.006, - "sigma_Cb": 0.00208 - } - }, - { - "rpi.contrast": - { - "ce_enable": 1, - "gamma_curve": - [ - 0, 0, - 1024, 5040, - 2048, 9338, - 3072, 12356, - 4096, 15312, - 5120, 18051, - 6144, 20790, - 7168, 23193, - 8192, 25744, - 9216, 27942, - 10240, 30035, - 11264, 32005, - 12288, 33975, - 13312, 35815, - 14336, 37600, - 15360, 39168, - 16384, 40642, - 18432, 43379, - 20480, 45749, - 22528, 47753, - 24576, 49621, - 26624, 51253, - 28672, 52698, - 30720, 53796, - 32768, 54876, - 36864, 57012, - 40960, 58656, - 45056, 59954, - 49152, 61183, - 53248, 62355, - 57344, 63419, - 61440, 64476, - 65535, 65535 - ] - } - }, - { - "rpi.ccm": - { - "ccms": [ - { - "ct": 2500, - "ccm": - [ - 1.70741, -0.05307, -0.65433, - -0.62822, 1.68836, -0.06014, - -0.04452, -1.87628, 2.92079 - ] - }, - { - "ct": 2803, - "ccm": - [ - 1.74383, -0.18731, -0.55652, - -0.56491, 1.67772, -0.11281, - -0.01522, -1.60635, 2.62157 - ] - }, - { - "ct": 2912, - "ccm": - [ - 1.75215, -0.22221, -0.52995, - -0.54568, 1.63522, -0.08954, - 0.02633, -1.56997, 2.54364 - ] - }, - { - "ct": 2914, - "ccm": - [ - 1.72423, -0.28939, -0.43484, - -0.55188, 1.62925, -0.07737, - 0.01959, -1.28661, 2.26702 - ] - }, - { - "ct": 3605, - "ccm": - [ - 1.80381, -0.43646, -0.36735, - -0.46505, 1.56814, -0.10309, - 0.00929, -1.00424, 1.99495 - ] - }, - { - "ct": 4540, - "ccm": - [ - 1.85263, -0.46545, -0.38719, - -0.44136, 1.68443, -0.24307, - 0.04108, -0.85599, 1.81491 - ] - }, - { - "ct": 5699, - "ccm": - [ - 1.98595, -0.63542, -0.35054, - -0.34623, 1.54146, -0.19522, - 0.00411, -0.70936, 1.70525 - ] - }, - { - "ct": 8625, - "ccm": - [ - 2.21637, -0.56663, -0.64974, - -0.41133, 1.96625, -0.55492, - -0.02307, -0.83529, 1.85837 - ] - } - ] - } - }, - { - "rpi.sharpen": { } - } - ] -} \ No newline at end of file diff --git a/src/ipa/raspberrypi/data/ov9281_mono.json b/src/ipa/raspberrypi/data/ov9281_mono.json deleted file mode 100644 index 37944c63..00000000 --- a/src/ipa/raspberrypi/data/ov9281_mono.json +++ /dev/null @@ -1,123 +0,0 @@ -{ - "version": 2.0, - "target": "bcm2835", - "algorithms": [ - { - "rpi.black_level": - { - "black_level": 4096 - } - }, - { - "rpi.lux": - { - "reference_shutter_speed": 2000, - "reference_gain": 1.0, - "reference_aperture": 1.0, - "reference_lux": 800, - "reference_Y": 20000 - } - }, - { - "rpi.noise": - { - "reference_constant": 0, - "reference_slope": 2.5 - } - }, - { - "rpi.sdn": { } - }, - { - "rpi.agc": - { - "metering_modes": - { - "centre-weighted": - { - "weights": [ 4, 4, 4, 2, 2, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0 ] - } - }, - "exposure_modes": - { - "normal": - { - "shutter": [ 100, 15000, 30000, 60000, 120000 ], - "gain": [ 1.0, 2.0, 3.0, 4.0, 8.0 ] - } - }, - "constraint_modes": - { - "normal": [ - { - "bound": "LOWER", - "q_lo": 0.98, - "q_hi": 1.0, - "y_target": - [ - 0, 0.4, - 1000, 0.4 - ] - } - ] - }, - "y_target": - [ - 0, 0.16, - 1000, 0.165, - 10000, 0.17 - ] - } - }, - { - "rpi.alsc": - { - "n_iter": 0, - "luminance_strength": 1.0, - "corner_strength": 1.5 - } - }, - { - "rpi.contrast": - { - "ce_enable": 0, - "gamma_curve": - [ - 0, 0, - 1024, 5040, - 2048, 9338, - 3072, 12356, - 4096, 15312, - 5120, 18051, - 6144, 20790, - 7168, 23193, - 8192, 25744, - 9216, 27942, - 10240, 30035, - 11264, 32005, - 12288, 33975, - 13312, 35815, - 14336, 37600, - 15360, 39168, - 16384, 40642, - 18432, 43379, - 20480, 45749, - 22528, 47753, - 24576, 49621, - 26624, 51253, - 28672, 52698, - 30720, 53796, - 32768, 54876, - 36864, 57012, - 40960, 58656, - 45056, 59954, - 49152, 61183, - 53248, 62355, - 57344, 63419, - 61440, 64476, - 65535, 65535 - ] - } - } - ] -} \ No newline at end of file diff --git a/src/ipa/raspberrypi/data/se327m12.json b/src/ipa/raspberrypi/data/se327m12.json deleted file mode 100644 index ee69caea..00000000 --- a/src/ipa/raspberrypi/data/se327m12.json +++ /dev/null @@ -1,418 +0,0 @@ -{ - "version": 2.0, - "target": "bcm2835", - "algorithms": [ - { - "rpi.black_level": - { - "black_level": 3840 - } - }, - { - "rpi.dpc": { } - }, - { - "rpi.lux": - { - "reference_shutter_speed": 6873, - "reference_gain": 1.0, - "reference_aperture": 1.0, - "reference_lux": 800, - "reference_Y": 12293 - } - }, - { - "rpi.noise": - { - "reference_constant": 0, - "reference_slope": 1.986 - } - }, - { - "rpi.geq": - { - "offset": 207, - "slope": 0.00539 - } - }, - { - "rpi.sdn": { } - }, - { - "rpi.awb": - { - "priors": [ - { - "lux": 0, - "prior": - [ - 2000, 1.0, - 3000, 0.0, - 13000, 0.0 - ] - }, - { - "lux": 800, - "prior": - [ - 2000, 0.0, - 6000, 2.0, - 13000, 2.0 - ] - }, - { - "lux": 1500, - "prior": - [ - 2000, 0.0, - 4000, 1.0, - 6000, 6.0, - 6500, 7.0, - 7000, 1.0, - 13000, 1.0 - ] - } - ], - "modes": - { - "auto": - { - "lo": 2500, - "hi": 8000 - }, - "incandescent": - { - "lo": 2500, - "hi": 3000 - }, - "tungsten": - { - "lo": 3000, - "hi": 3500 - }, - "fluorescent": - { - "lo": 4000, - "hi": 4700 - }, - "indoor": - { - "lo": 3000, - "hi": 5000 - }, - "daylight": - { - "lo": 5500, - "hi": 6500 - }, - "cloudy": - { - "lo": 7000, - "hi": 8600 - } - }, - "bayes": 1, - "ct_curve": - [ - 2900.0, 0.9217, 0.3657, - 3600.0, 0.7876, 0.4651, - 4600.0, 0.6807, 0.5684, - 5800.0, 0.5937, 0.6724, - 8100.0, 0.5447, 0.7403 - ], - "sensitivity_r": 1.0, - "sensitivity_b": 1.0, - "transverse_pos": 0.0162, - "transverse_neg": 0.0204 - } - }, - { - "rpi.agc": - { - "metering_modes": - { - "centre-weighted": - { - "weights": [ 3, 3, 3, 2, 2, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0 ] - }, - "spot": - { - "weights": [ 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ] - }, - "matrix": - { - "weights": [ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ] - } - }, - "exposure_modes": - { - "normal": - { - "shutter": [ 100, 10000, 30000, 60000, 120000 ], - "gain": [ 1.0, 2.0, 4.0, 6.0, 8.0 ] - }, - "short": - { - "shutter": [ 100, 5000, 10000, 20000, 120000 ], - "gain": [ 1.0, 2.0, 4.0, 6.0, 8.0 ] - } - }, - "constraint_modes": - { - "normal": [ - { - "bound": "LOWER", - "q_lo": 0.98, - "q_hi": 1.0, - "y_target": - [ - 0, 0.5, - 1000, 0.5 - ] - } - ], - "highlight": [ - { - "bound": "LOWER", - "q_lo": 0.98, - "q_hi": 1.0, - "y_target": - [ - 0, 0.5, - 1000, 0.5 - ] - }, - { - "bound": "UPPER", - "q_lo": 0.98, - "q_hi": 1.0, - "y_target": - [ - 0, 0.8, - 1000, 0.8 - ] - } - ] - }, - "y_target": - [ - 0, 0.16, - 1000, 0.165, - 10000, 0.17 - ] - } - }, - { - "rpi.alsc": - { - "omega": 1.3, - "n_iter": 100, - "luminance_strength": 0.5, - "calibrations_Cr": [ - { - "ct": 4000, - "table": - [ - 1.481, 1.471, 1.449, 1.429, 1.416, 1.404, 1.394, 1.389, 1.389, 1.389, 1.392, 1.397, 1.404, 1.416, 1.429, 1.437, - 1.472, 1.456, 1.436, 1.418, 1.405, 1.394, 1.389, 1.384, 1.382, 1.382, 1.386, 1.388, 1.398, 1.407, 1.422, 1.429, - 1.465, 1.443, 1.426, 1.411, 1.397, 1.389, 1.383, 1.377, 1.377, 1.377, 1.379, 1.384, 1.388, 1.398, 1.411, 1.422, - 1.462, 1.441, 1.423, 1.409, 1.395, 1.385, 1.379, 1.376, 1.374, 1.374, 1.375, 1.379, 1.384, 1.394, 1.407, 1.418, - 1.461, 1.439, 1.421, 1.407, 1.394, 1.385, 1.381, 1.376, 1.373, 1.373, 1.373, 1.376, 1.381, 1.389, 1.403, 1.415, - 1.461, 1.439, 1.419, 1.404, 1.392, 1.384, 1.379, 1.376, 1.373, 1.372, 1.374, 1.375, 1.379, 1.389, 1.401, 1.413, - 1.461, 1.438, 1.419, 1.402, 1.389, 1.383, 1.377, 1.375, 1.373, 1.372, 1.372, 1.375, 1.381, 1.388, 1.401, 1.414, - 1.462, 1.438, 1.419, 1.403, 1.391, 1.381, 1.377, 1.374, 1.373, 1.373, 1.374, 1.376, 1.381, 1.389, 1.401, 1.414, - 1.462, 1.441, 1.423, 1.405, 1.392, 1.383, 1.377, 1.374, 1.373, 1.372, 1.373, 1.376, 1.382, 1.391, 1.402, 1.414, - 1.465, 1.444, 1.424, 1.407, 1.393, 1.382, 1.378, 1.373, 1.369, 1.369, 1.372, 1.375, 1.381, 1.389, 1.402, 1.417, - 1.469, 1.449, 1.427, 1.413, 1.396, 1.384, 1.381, 1.375, 1.371, 1.371, 1.373, 1.377, 1.385, 1.393, 1.407, 1.422, - 1.474, 1.456, 1.436, 1.419, 1.407, 1.391, 1.383, 1.379, 1.377, 1.377, 1.378, 1.381, 1.391, 1.404, 1.422, 1.426 - ] - }, - { - "ct": 5000, - "table": - [ - 1.742, 1.721, 1.689, 1.661, 1.639, 1.623, 1.613, 1.609, 1.607, 1.606, 1.609, 1.617, 1.626, 1.641, 1.665, 1.681, - 1.728, 1.703, 1.672, 1.645, 1.631, 1.614, 1.602, 1.599, 1.596, 1.597, 1.601, 1.608, 1.618, 1.631, 1.653, 1.671, - 1.713, 1.691, 1.658, 1.635, 1.618, 1.606, 1.595, 1.591, 1.588, 1.588, 1.591, 1.601, 1.608, 1.624, 1.641, 1.658, - 1.707, 1.681, 1.651, 1.627, 1.613, 1.599, 1.591, 1.585, 1.583, 1.584, 1.587, 1.591, 1.601, 1.615, 1.633, 1.655, - 1.699, 1.672, 1.644, 1.622, 1.606, 1.593, 1.586, 1.581, 1.579, 1.581, 1.583, 1.587, 1.597, 1.611, 1.631, 1.652, - 1.697, 1.665, 1.637, 1.617, 1.601, 1.589, 1.584, 1.579, 1.577, 1.578, 1.581, 1.585, 1.597, 1.607, 1.627, 1.652, - 1.697, 1.662, 1.634, 1.613, 1.599, 1.591, 1.583, 1.578, 1.576, 1.576, 1.579, 1.586, 1.597, 1.607, 1.628, 1.653, - 1.697, 1.662, 1.633, 1.613, 1.598, 1.589, 1.582, 1.578, 1.576, 1.577, 1.582, 1.589, 1.598, 1.611, 1.635, 1.655, - 1.701, 1.666, 1.636, 1.616, 1.602, 1.589, 1.583, 1.578, 1.577, 1.581, 1.583, 1.591, 1.601, 1.617, 1.639, 1.659, - 1.708, 1.671, 1.641, 1.618, 1.603, 1.591, 1.584, 1.581, 1.578, 1.581, 1.585, 1.594, 1.604, 1.622, 1.646, 1.666, - 1.714, 1.681, 1.648, 1.622, 1.608, 1.599, 1.591, 1.584, 1.583, 1.584, 1.589, 1.599, 1.614, 1.629, 1.653, 1.673, - 1.719, 1.691, 1.659, 1.631, 1.618, 1.606, 1.596, 1.591, 1.591, 1.593, 1.599, 1.608, 1.623, 1.642, 1.665, 1.681 - ] - } - ], - "calibrations_Cb": [ - { - "ct": 4000, - "table": - [ - 2.253, 2.267, 2.289, 2.317, 2.342, 2.359, 2.373, 2.381, 2.381, 2.378, 2.368, 2.361, 2.344, 2.337, 2.314, 2.301, - 2.262, 2.284, 2.314, 2.335, 2.352, 2.371, 2.383, 2.391, 2.393, 2.391, 2.381, 2.368, 2.361, 2.342, 2.322, 2.308, - 2.277, 2.303, 2.321, 2.346, 2.364, 2.381, 2.391, 2.395, 2.397, 2.397, 2.395, 2.381, 2.367, 2.354, 2.332, 2.321, - 2.277, 2.304, 2.327, 2.349, 2.369, 2.388, 2.393, 2.396, 2.396, 2.398, 2.396, 2.391, 2.376, 2.359, 2.339, 2.328, - 2.279, 2.311, 2.327, 2.354, 2.377, 2.389, 2.393, 2.397, 2.397, 2.398, 2.395, 2.393, 2.382, 2.363, 2.344, 2.332, - 2.282, 2.311, 2.329, 2.354, 2.377, 2.386, 2.396, 2.396, 2.395, 2.396, 2.397, 2.394, 2.383, 2.367, 2.346, 2.333, - 2.283, 2.314, 2.333, 2.353, 2.375, 2.389, 2.394, 2.395, 2.395, 2.395, 2.396, 2.394, 2.386, 2.368, 2.354, 2.336, - 2.287, 2.309, 2.331, 2.352, 2.373, 2.386, 2.394, 2.395, 2.395, 2.396, 2.396, 2.394, 2.384, 2.371, 2.354, 2.339, - 2.289, 2.307, 2.326, 2.347, 2.369, 2.385, 2.392, 2.397, 2.398, 2.398, 2.397, 2.392, 2.383, 2.367, 2.352, 2.337, - 2.286, 2.303, 2.322, 2.342, 2.361, 2.379, 2.389, 2.394, 2.397, 2.398, 2.396, 2.389, 2.381, 2.366, 2.346, 2.332, - 2.284, 2.291, 2.312, 2.329, 2.351, 2.372, 2.381, 2.389, 2.393, 2.394, 2.389, 2.385, 2.374, 2.362, 2.338, 2.325, - 2.283, 2.288, 2.305, 2.319, 2.339, 2.365, 2.374, 2.381, 2.384, 2.386, 2.385, 2.379, 2.368, 2.342, 2.325, 2.318 - ] - }, - { - "ct": 5000, - "table": - [ - 1.897, 1.919, 1.941, 1.969, 1.989, 2.003, 2.014, 2.019, 2.019, 2.017, 2.014, 2.008, 1.999, 1.988, 1.968, 1.944, - 1.914, 1.932, 1.957, 1.982, 1.998, 2.014, 2.023, 2.029, 2.031, 2.029, 2.022, 2.014, 2.006, 1.995, 1.976, 1.955, - 1.925, 1.951, 1.974, 1.996, 2.013, 2.027, 2.035, 2.039, 2.039, 2.038, 2.035, 2.026, 2.015, 2.002, 1.984, 1.963, - 1.932, 1.958, 1.986, 2.007, 2.024, 2.034, 2.041, 2.041, 2.045, 2.045, 2.042, 2.033, 2.023, 2.009, 1.995, 1.971, - 1.942, 1.964, 1.994, 2.012, 2.029, 2.038, 2.043, 2.046, 2.047, 2.046, 2.045, 2.039, 2.029, 2.014, 1.997, 1.977, - 1.946, 1.974, 1.999, 2.015, 2.031, 2.041, 2.046, 2.047, 2.048, 2.047, 2.044, 2.041, 2.031, 2.019, 1.999, 1.978, - 1.948, 1.975, 2.002, 2.018, 2.031, 2.041, 2.046, 2.047, 2.048, 2.048, 2.045, 2.041, 2.029, 2.019, 1.998, 1.978, - 1.948, 1.973, 2.002, 2.018, 2.029, 2.042, 2.045, 2.048, 2.048, 2.048, 2.044, 2.037, 2.027, 2.014, 1.993, 1.978, - 1.945, 1.969, 1.998, 2.015, 2.028, 2.037, 2.045, 2.046, 2.047, 2.044, 2.039, 2.033, 2.022, 2.008, 1.989, 1.971, - 1.939, 1.964, 1.991, 2.011, 2.024, 2.032, 2.036, 2.042, 2.042, 2.039, 2.035, 2.024, 2.012, 1.998, 1.977, 1.964, - 1.932, 1.953, 1.981, 2.006, 2.016, 2.024, 2.028, 2.031, 2.034, 2.031, 2.024, 2.015, 2.005, 1.989, 1.966, 1.955, - 1.928, 1.944, 1.973, 1.999, 2.007, 2.016, 2.019, 2.025, 2.026, 2.025, 2.017, 2.008, 1.997, 1.975, 1.958, 1.947 - ] - } - ], - "luminance_lut": - [ - 1.877, 1.597, 1.397, 1.269, 1.191, 1.131, 1.093, 1.078, 1.071, 1.069, 1.086, 1.135, 1.221, 1.331, 1.474, 1.704, - 1.749, 1.506, 1.334, 1.229, 1.149, 1.088, 1.058, 1.053, 1.051, 1.046, 1.053, 1.091, 1.163, 1.259, 1.387, 1.587, - 1.661, 1.451, 1.295, 1.195, 1.113, 1.061, 1.049, 1.048, 1.047, 1.049, 1.049, 1.066, 1.124, 1.211, 1.333, 1.511, - 1.615, 1.411, 1.267, 1.165, 1.086, 1.052, 1.047, 1.047, 1.047, 1.049, 1.052, 1.056, 1.099, 1.181, 1.303, 1.471, - 1.576, 1.385, 1.252, 1.144, 1.068, 1.049, 1.044, 1.044, 1.045, 1.049, 1.053, 1.054, 1.083, 1.163, 1.283, 1.447, - 1.561, 1.373, 1.245, 1.135, 1.064, 1.049, 1.044, 1.044, 1.044, 1.046, 1.048, 1.054, 1.073, 1.153, 1.271, 1.432, - 1.571, 1.377, 1.242, 1.137, 1.066, 1.055, 1.052, 1.051, 1.051, 1.049, 1.047, 1.048, 1.068, 1.148, 1.271, 1.427, - 1.582, 1.396, 1.259, 1.156, 1.085, 1.068, 1.059, 1.054, 1.049, 1.045, 1.041, 1.043, 1.074, 1.157, 1.284, 1.444, - 1.623, 1.428, 1.283, 1.178, 1.105, 1.074, 1.069, 1.063, 1.056, 1.048, 1.046, 1.051, 1.094, 1.182, 1.311, 1.473, - 1.691, 1.471, 1.321, 1.213, 1.135, 1.088, 1.073, 1.069, 1.063, 1.059, 1.053, 1.071, 1.129, 1.222, 1.351, 1.521, - 1.808, 1.543, 1.371, 1.253, 1.174, 1.118, 1.085, 1.072, 1.067, 1.064, 1.071, 1.106, 1.176, 1.274, 1.398, 1.582, - 1.969, 1.666, 1.447, 1.316, 1.223, 1.166, 1.123, 1.094, 1.089, 1.097, 1.118, 1.163, 1.239, 1.336, 1.471, 1.681 - ], - "sigma": 0.00218, - "sigma_Cb": 0.00194 - } - }, - { - "rpi.contrast": - { - "ce_enable": 1, - "gamma_curve": - [ - 0, 0, - 1024, 5040, - 2048, 9338, - 3072, 12356, - 4096, 15312, - 5120, 18051, - 6144, 20790, - 7168, 23193, - 8192, 25744, - 9216, 27942, - 10240, 30035, - 11264, 32005, - 12288, 33975, - 13312, 35815, - 14336, 37600, - 15360, 39168, - 16384, 40642, - 18432, 43379, - 20480, 45749, - 22528, 47753, - 24576, 49621, - 26624, 51253, - 28672, 52698, - 30720, 53796, - 32768, 54876, - 36864, 57012, - 40960, 58656, - 45056, 59954, - 49152, 61183, - 53248, 62355, - 57344, 63419, - 61440, 64476, - 65535, 65535 - ] - } - }, - { - "rpi.ccm": - { - "ccms": [ - { - "ct": 2900, - "ccm": - [ - 1.44924, -0.12935, -0.31989, - -0.65839, 1.95441, -0.29602, - 0.18344, -1.22282, 2.03938 - ] - }, - { - "ct": 3000, - "ccm": - [ - 1.38736, 0.07714, -0.46451, - -0.59691, 1.84335, -0.24644, - 0.10092, -1.30441, 2.20349 - ] - }, - { - "ct": 3600, - "ccm": - [ - 1.51261, -0.27921, -0.23339, - -0.55129, 1.83241, -0.28111, - 0.11649, -0.93195, 1.81546 - ] - }, - { - "ct": 4600, - "ccm": - [ - 1.47082, -0.18523, -0.28559, - -0.48923, 1.95126, -0.46203, - 0.07951, -0.83987, 1.76036 - ] - }, - { - "ct": 5800, - "ccm": - [ - 1.57294, -0.36229, -0.21065, - -0.42272, 1.80305, -0.38032, - 0.03671, -0.66862, 1.63191 - ] - }, - { - "ct": 8100, - "ccm": - [ - 1.58803, -0.09912, -0.48891, - -0.42594, 2.22303, -0.79709, - -0.00621, -0.90516, 1.91137 - ] - } - ] - } - }, - { - "rpi.sharpen": - { - "threshold": 2.0, - "strength": 0.5, - "limit": 0.5 - } - } - ] -} \ No newline at end of file diff --git a/src/ipa/raspberrypi/data/uncalibrated.json b/src/ipa/raspberrypi/data/uncalibrated.json deleted file mode 100644 index 13eb3f17..00000000 --- a/src/ipa/raspberrypi/data/uncalibrated.json +++ /dev/null @@ -1,118 +0,0 @@ -{ - "version": 2.0, - "target": "bcm2835", - "algorithms": [ - { - "rpi.black_level": - { - "black_level": 4096 - } - }, - { - "rpi.awb": - { - "use_derivatives": 0, - "bayes": 0 - } - }, - { - "rpi.agc": - { - "metering_modes": - { - "centre-weighted": - { - "weights": [ 4, 4, 4, 2, 2, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0 ] - } - }, - "exposure_modes": - { - "normal": - { - "shutter": [ 100, 15000, 30000, 60000, 120000 ], - "gain": [ 1.0, 2.0, 3.0, 4.0, 6.0 ] - } - }, - "constraint_modes": - { - "normal": [ - { - "bound": "LOWER", - "q_lo": 0.98, - "q_hi": 1.0, - "y_target": - [ - 0, 0.4, - 1000, 0.4 - ] - } - ] - }, - "y_target": - [ - 0, 0.16, - 1000, 0.165, - 10000, 0.17 - ] - } - }, - { - "rpi.ccm": - { - "ccms": [ - { - "ct": 4000, - "ccm": - [ - 2.0, -1.0, 0.0, - -0.5, 2.0, -0.5, - 0, -1.0, 2.0 - ] - } - ] - } - }, - { - "rpi.contrast": - { - "ce_enable": 0, - "gamma_curve": - [ - 0, 0, - 1024, 5040, - 2048, 9338, - 3072, 12356, - 4096, 15312, - 5120, 18051, - 6144, 20790, - 7168, 23193, - 8192, 25744, - 9216, 27942, - 10240, 30035, - 11264, 32005, - 12288, 33975, - 13312, 35815, - 14336, 37600, - 15360, 39168, - 16384, 40642, - 18432, 43379, - 20480, 45749, - 22528, 47753, - 24576, 49621, - 26624, 51253, - 28672, 52698, - 30720, 53796, - 32768, 54876, - 36864, 57012, - 40960, 58656, - 45056, 59954, - 49152, 61183, - 53248, 62355, - 57344, 63419, - 61440, 64476, - 65535, 65535 - ] - } - } - ] -} \ No newline at end of file diff --git a/src/ipa/raspberrypi/md_parser.h b/src/ipa/raspberrypi/md_parser.h deleted file mode 100644 index 77d557aa..00000000 --- a/src/ipa/raspberrypi/md_parser.h +++ /dev/null @@ -1,155 +0,0 @@ -/* SPDX-License-Identifier: BSD-2-Clause */ -/* - * Copyright (C) 2019, Raspberry Pi Ltd - * - * md_parser.h - image sensor metadata parser interface - */ -#pragma once - -#include -#include -#include -#include - -#include - -/* - * Camera metadata parser class. Usage as shown below. - * - * Setup: - * - * Usually the metadata parser will be made as part of the CamHelper class so - * application code doesn't have to worry which kind to instantiate. But for - * the sake of example let's suppose we're parsing imx219 metadata. - * - * MdParser *parser = new MdParserSmia({ expHiReg, expLoReg, gainReg }); - * parser->SetBitsPerPixel(bpp); - * parser->SetLineLengthBytes(pitch); - * parser->SetNumLines(2); - * - * Note 1: if you don't know how many lines there are, the size of the input - * buffer is used as a limit instead. - * - * Note 2: if you don't know the line length, you can leave the line length unset - * (or set to zero) and the parser will hunt for the line start instead. - * - * Then on every frame: - * - * RegisterMap registers; - * if (parser->Parse(buffer, registers) != MdParser::OK) - * much badness; - * Metadata metadata; - * CamHelper::PopulateMetadata(registers, metadata); - * - * (Note that the CamHelper class converts to/from exposure lines and time, - * and gain_code / actual gain.) - * - * If you suspect your embedded data may have changed its layout, change any line - * lengths, number of lines, bits per pixel etc. that are different, and - * then: - * - * parser->Reset(); - * - * before calling Parse again. - */ - -namespace RPiController { - -/* Abstract base class from which other metadata parsers are derived. */ - -class MdParser -{ -public: - using RegisterMap = std::map; - - /* - * Parser status codes: - * OK - success - * NOTFOUND - value such as exposure or gain was not found - * ERROR - all other errors - */ - enum Status { - OK = 0, - NOTFOUND = 1, - ERROR = 2 - }; - - MdParser() - : reset_(true), bitsPerPixel_(0), numLines_(0), lineLengthBytes_(0) - { - } - - virtual ~MdParser() = default; - - void reset() - { - reset_ = true; - } - - void setBitsPerPixel(int bpp) - { - bitsPerPixel_ = bpp; - } - - void setNumLines(unsigned int numLines) - { - numLines_ = numLines; - } - - void setLineLengthBytes(unsigned int numBytes) - { - lineLengthBytes_ = numBytes; - } - - virtual Status parse(libcamera::Span buffer, - RegisterMap ®isters) = 0; - -protected: - bool reset_; - int bitsPerPixel_; - unsigned int numLines_; - unsigned int lineLengthBytes_; -}; - -/* - * This isn't a full implementation of a metadata parser for SMIA sensors, - * however, it does provide the findRegs function which will prove useful and - * make it easier to implement parsers for other SMIA-like sensors (see - * md_parser_imx219.cpp for an example). - */ - -class MdParserSmia final : public MdParser -{ -public: - MdParserSmia(std::initializer_list registerList); - - MdParser::Status parse(libcamera::Span buffer, - RegisterMap ®isters) override; - -private: - /* Maps register address to offset in the buffer. */ - using OffsetMap = std::map>; - - /* - * Note that error codes > 0 are regarded as non-fatal; codes < 0 - * indicate a bad data buffer. Status codes are: - * ParseOk - found all registers, much happiness - * MissingRegs - some registers found; should this be a hard error? - * The remaining codes are all hard errors. - */ - enum ParseStatus { - ParseOk = 0, - MissingRegs = 1, - NoLineStart = -1, - IllegalTag = -2, - BadDummy = -3, - BadLineEnd = -4, - BadPadding = -5 - }; - - ParseStatus findRegs(libcamera::Span buffer); - - OffsetMap offsets_; -}; - -} /* namespace RPi */ diff --git a/src/ipa/raspberrypi/md_parser_smia.cpp b/src/ipa/raspberrypi/md_parser_smia.cpp deleted file mode 100644 index 210787ed..00000000 --- a/src/ipa/raspberrypi/md_parser_smia.cpp +++ /dev/null @@ -1,149 +0,0 @@ -/* SPDX-License-Identifier: BSD-2-Clause */ -/* - * Copyright (C) 2019-2021, Raspberry Pi Ltd - * - * md_parser_smia.cpp - SMIA specification based embedded data parser - */ - -#include -#include "md_parser.h" - -using namespace RPiController; -using namespace libcamera; - -/* - * This function goes through the embedded data to find the offsets (not - * values!), in the data block, where the values of the given registers can - * subsequently be found. - * - * Embedded data tag bytes, from Sony IMX219 datasheet but general to all SMIA - * sensors, I think. - */ - -constexpr unsigned int LineStart = 0x0a; -constexpr unsigned int LineEndTag = 0x07; -constexpr unsigned int RegHiBits = 0xaa; -constexpr unsigned int RegLowBits = 0xa5; -constexpr unsigned int RegValue = 0x5a; -constexpr unsigned int RegSkip = 0x55; - -MdParserSmia::MdParserSmia(std::initializer_list registerList) -{ - for (auto r : registerList) - offsets_[r] = {}; -} - -MdParser::Status MdParserSmia::parse(libcamera::Span buffer, - RegisterMap ®isters) -{ - if (reset_) { - /* - * Search again through the metadata for all the registers - * requested. - */ - ASSERT(bitsPerPixel_); - - for (const auto &kv : offsets_) - offsets_[kv.first] = {}; - - ParseStatus ret = findRegs(buffer); - /* - * > 0 means "worked partially but parse again next time", - * < 0 means "hard error". - * - * In either case, we retry parsing on the next frame. - */ - if (ret != ParseOk) - return ERROR; - - reset_ = false; - } - - /* Populate the register values requested. */ - registers.clear(); - for (const auto &[reg, offset] : offsets_) { - if (!offset) { - reset_ = true; - return NOTFOUND; - } - registers[reg] = buffer[offset.value()]; - } - - return OK; -} - -MdParserSmia::ParseStatus MdParserSmia::findRegs(libcamera::Span buffer) -{ - ASSERT(offsets_.size()); - - if (buffer[0] != LineStart) - return NoLineStart; - - unsigned int currentOffset = 1; /* after the LineStart */ - unsigned int currentLineStart = 0, currentLine = 0; - unsigned int regNum = 0, regsDone = 0; - - while (1) { - int tag = buffer[currentOffset++]; - - if ((bitsPerPixel_ == 10 && - (currentOffset + 1 - currentLineStart) % 5 == 0) || - (bitsPerPixel_ == 12 && - (currentOffset + 1 - currentLineStart) % 3 == 0)) { - if (buffer[currentOffset++] != RegSkip) - return BadDummy; - } - - int dataByte = buffer[currentOffset++]; - - if (tag == LineEndTag) { - if (dataByte != LineEndTag) - return BadLineEnd; - - if (numLines_ && ++currentLine == numLines_) - return MissingRegs; - - if (lineLengthBytes_) { - currentOffset = currentLineStart + lineLengthBytes_; - - /* Require whole line to be in the buffer (if buffer size set). */ - if (buffer.size() && - currentOffset + lineLengthBytes_ > buffer.size()) - return MissingRegs; - - if (buffer[currentOffset] != LineStart) - return NoLineStart; - } else { - /* allow a zero line length to mean "hunt for the next line" */ - while (currentOffset < buffer.size() && - buffer[currentOffset] != LineStart) - currentOffset++; - - if (currentOffset == buffer.size()) - return NoLineStart; - } - - /* inc currentOffset to after LineStart */ - currentLineStart = currentOffset++; - } else { - if (tag == RegHiBits) - regNum = (regNum & 0xff) | (dataByte << 8); - else if (tag == RegLowBits) - regNum = (regNum & 0xff00) | dataByte; - else if (tag == RegSkip) - regNum++; - else if (tag == RegValue) { - auto reg = offsets_.find(regNum); - - if (reg != offsets_.end()) { - offsets_[regNum] = currentOffset - 1; - - if (++regsDone == offsets_.size()) - return ParseOk; - } - regNum++; - } else - return IllegalTag; - } - } -} diff --git a/src/ipa/raspberrypi/meson.build b/src/ipa/raspberrypi/meson.build deleted file mode 100644 index 95437cbc..00000000 --- a/src/ipa/raspberrypi/meson.build +++ /dev/null @@ -1,68 +0,0 @@ -# SPDX-License-Identifier: CC0-1.0 - -ipa_name = 'ipa_rpi' - -rpi_ipa_deps = [ - libcamera_private, - libatomic, -] - -rpi_ipa_includes = [ - ipa_includes, - libipa_includes, - include_directories('controller') -] - -rpi_ipa_sources = files([ - 'raspberrypi.cpp', - 'md_parser_smia.cpp', - 'cam_helper.cpp', - 'cam_helper_ov5647.cpp', - 'cam_helper_imx219.cpp', - 'cam_helper_imx290.cpp', - 'cam_helper_imx296.cpp', - 'cam_helper_imx477.cpp', - 'cam_helper_imx519.cpp', - 'cam_helper_imx708.cpp', - 'cam_helper_ov9281.cpp', - 'controller/controller.cpp', - 'controller/histogram.cpp', - 'controller/algorithm.cpp', - 'controller/rpi/af.cpp', - 'controller/rpi/alsc.cpp', - 'controller/rpi/awb.cpp', - 'controller/rpi/sharpen.cpp', - 'controller/rpi/black_level.cpp', - 'controller/rpi/geq.cpp', - 'controller/rpi/noise.cpp', - 'controller/rpi/lux.cpp', - 'controller/rpi/agc.cpp', - 'controller/rpi/dpc.cpp', - 'controller/rpi/ccm.cpp', - 'controller/rpi/contrast.cpp', - 'controller/rpi/sdn.cpp', - 'controller/pwl.cpp', - 'controller/device_status.cpp', -]) - -mod = shared_module(ipa_name, - [rpi_ipa_sources, libcamera_generated_ipa_headers], - name_prefix : '', - include_directories : rpi_ipa_includes, - dependencies : rpi_ipa_deps, - link_with : libipa, - install : true, - install_dir : ipa_install_dir) - -if ipa_sign_module - custom_target(ipa_name + '.so.sign', - input : mod, - output : ipa_name + '.so.sign', - command : [ipa_sign, ipa_priv_key, '@INPUT@', '@OUTPUT@'], - install : false, - build_by_default : true) -endif - -subdir('data') - -ipa_names += ipa_name diff --git a/src/ipa/raspberrypi/raspberrypi.cpp b/src/ipa/raspberrypi/raspberrypi.cpp deleted file mode 100644 index 9c29fa9a..00000000 --- a/src/ipa/raspberrypi/raspberrypi.cpp +++ /dev/null @@ -1,1853 +0,0 @@ -/* SPDX-License-Identifier: BSD-2-Clause */ -/* - * Copyright (C) 2019-2021, Raspberry Pi Ltd - * - * rpi.cpp - Raspberry Pi Image Processing Algorithms - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include - -#include -#include -#include -#include - -#include -#include -#include - -#include "libcamera/internal/mapped_framebuffer.h" - -#include "af_algorithm.h" -#include "af_status.h" -#include "agc_algorithm.h" -#include "agc_status.h" -#include "alsc_status.h" -#include "awb_algorithm.h" -#include "awb_status.h" -#include "black_level_status.h" -#include "cam_helper.h" -#include "ccm_algorithm.h" -#include "ccm_status.h" -#include "contrast_algorithm.h" -#include "contrast_status.h" -#include "controller.h" -#include "denoise_algorithm.h" -#include "denoise_status.h" -#include "dpc_status.h" -#include "geq_status.h" -#include "lux_status.h" -#include "metadata.h" -#include "sharpen_algorithm.h" -#include "sharpen_status.h" -#include "statistics.h" - -namespace libcamera { - -using namespace std::literals::chrono_literals; -using utils::Duration; - -/* Number of metadata objects available in the context list. */ -constexpr unsigned int numMetadataContexts = 16; - -/* Number of frame length times to hold in the queue. */ -constexpr unsigned int FrameLengthsQueueSize = 10; - -/* Configure the sensor with these values initially. */ -constexpr double defaultAnalogueGain = 1.0; -constexpr Duration defaultExposureTime = 20.0ms; -constexpr Duration defaultMinFrameDuration = 1.0s / 30.0; -constexpr Duration defaultMaxFrameDuration = 250.0s; - -/* - * Determine the minimum allowable inter-frame duration to run the controller - * algorithms. If the pipeline handler provider frames at a rate higher than this, - * we rate-limit the controller Prepare() and Process() calls to lower than or - * equal to this rate. - */ -constexpr Duration controllerMinFrameDuration = 1.0s / 30.0; - -/* List of controls handled by the Raspberry Pi IPA */ -static const ControlInfoMap::Map ipaControls{ - { &controls::AeEnable, ControlInfo(false, true) }, - { &controls::ExposureTime, ControlInfo(0, 66666) }, - { &controls::AnalogueGain, ControlInfo(1.0f, 16.0f) }, - { &controls::AeMeteringMode, ControlInfo(controls::AeMeteringModeValues) }, - { &controls::AeConstraintMode, ControlInfo(controls::AeConstraintModeValues) }, - { &controls::AeExposureMode, ControlInfo(controls::AeExposureModeValues) }, - { &controls::ExposureValue, ControlInfo(-8.0f, 8.0f, 0.0f) }, - { &controls::AwbEnable, ControlInfo(false, true) }, - { &controls::ColourGains, ControlInfo(0.0f, 32.0f) }, - { &controls::AwbMode, ControlInfo(controls::AwbModeValues) }, - { &controls::Brightness, ControlInfo(-1.0f, 1.0f, 0.0f) }, - { &controls::Contrast, ControlInfo(0.0f, 32.0f, 1.0f) }, - { &controls::Saturation, ControlInfo(0.0f, 32.0f, 1.0f) }, - { &controls::Sharpness, ControlInfo(0.0f, 16.0f, 1.0f) }, - { &controls::ColourCorrectionMatrix, ControlInfo(-16.0f, 16.0f) }, - { &controls::ScalerCrop, ControlInfo(Rectangle{}, Rectangle(65535, 65535, 65535, 65535), Rectangle{}) }, - { &controls::FrameDurationLimits, ControlInfo(INT64_C(33333), INT64_C(120000)) }, - { &controls::draft::NoiseReductionMode, ControlInfo(controls::draft::NoiseReductionModeValues) } -}; - -/* IPA controls handled conditionally, if the lens has a focus control */ -static const ControlInfoMap::Map ipaAfControls{ - { &controls::AfMode, ControlInfo(controls::AfModeValues) }, - { &controls::AfRange, ControlInfo(controls::AfRangeValues) }, - { &controls::AfSpeed, ControlInfo(controls::AfSpeedValues) }, - { &controls::AfMetering, ControlInfo(controls::AfMeteringValues) }, - { &controls::AfWindows, ControlInfo(Rectangle{}, Rectangle(65535, 65535, 65535, 65535), Rectangle{}) }, - { &controls::AfTrigger, ControlInfo(controls::AfTriggerValues) }, - { &controls::AfPause, ControlInfo(controls::AfPauseValues) }, - { &controls::LensPosition, ControlInfo(0.0f, 32.0f, 1.0f) } -}; - -LOG_DEFINE_CATEGORY(IPARPI) - -namespace ipa::RPi { - -class IPARPi : public IPARPiInterface -{ -public: - IPARPi() - : controller_(), frameCount_(0), checkCount_(0), mistrustCount_(0), - lastRunTimestamp_(0), lsTable_(nullptr), firstStart_(true), - lastTimeout_(0s) - { - } - - ~IPARPi() - { - if (lsTable_) - munmap(lsTable_, MaxLsGridSize); - } - - int init(const IPASettings &settings, bool lensPresent, IPAInitResult *result) override; - void start(const ControlList &controls, StartConfig *startConfig) override; - void stop() override {} - - int configure(const IPACameraSensorInfo &sensorInfo, const IPAConfig &data, - ControlList *controls, IPAConfigResult *result) override; - void mapBuffers(const std::vector &buffers) override; - void unmapBuffers(const std::vector &ids) override; - void signalStatReady(const uint32_t bufferId, uint32_t ipaContext) override; - void signalQueueRequest(const ControlList &controls) override; - void signalIspPrepare(const ISPConfig &data) override; - -private: - void setMode(const IPACameraSensorInfo &sensorInfo); - bool validateSensorControls(); - bool validateIspControls(); - bool validateLensControls(); - void queueRequest(const ControlList &controls); - void returnEmbeddedBuffer(unsigned int bufferId); - void prepareISP(const ISPConfig &data); - void reportMetadata(unsigned int ipaContext); - void fillDeviceStatus(const ControlList &sensorControls, unsigned int ipaContext); - RPiController::StatisticsPtr fillStatistics(bcm2835_isp_stats *stats) const; - void processStats(unsigned int bufferId, unsigned int ipaContext); - void setCameraTimeoutValue(); - void applyFrameDurations(Duration minFrameDuration, Duration maxFrameDuration); - void applyAGC(const struct AgcStatus *agcStatus, ControlList &ctrls); - void applyAWB(const struct AwbStatus *awbStatus, ControlList &ctrls); - void applyDG(const struct AgcStatus *dgStatus, ControlList &ctrls); - void applyCCM(const struct CcmStatus *ccmStatus, ControlList &ctrls); - void applyBlackLevel(const struct BlackLevelStatus *blackLevelStatus, ControlList &ctrls); - void applyGamma(const struct ContrastStatus *contrastStatus, ControlList &ctrls); - void applyGEQ(const struct GeqStatus *geqStatus, ControlList &ctrls); - void applyDenoise(const struct DenoiseStatus *denoiseStatus, ControlList &ctrls); - void applySharpen(const struct SharpenStatus *sharpenStatus, ControlList &ctrls); - void applyDPC(const struct DpcStatus *dpcStatus, ControlList &ctrls); - void applyLS(const struct AlscStatus *lsStatus, ControlList &ctrls); - void applyAF(const struct AfStatus *afStatus, ControlList &lensCtrls); - void resampleTable(uint16_t dest[], const std::vector &src, int destW, int destH); - - std::map buffers_; - - ControlInfoMap sensorCtrls_; - ControlInfoMap ispCtrls_; - ControlInfoMap lensCtrls_; - bool lensPresent_; - ControlList libcameraMetadata_; - - /* Camera sensor params. */ - CameraMode mode_; - - /* Raspberry Pi controller specific defines. */ - std::unique_ptr helper_; - RPiController::Controller controller_; - std::array rpiMetadata_; - - /* - * We count frames to decide if the frame must be hidden (e.g. from - * display) or mistrusted (i.e. not given to the control algos). - */ - uint64_t frameCount_; - - /* For checking the sequencing of Prepare/Process calls. */ - uint64_t checkCount_; - - /* How many frames we should avoid running control algos on. */ - unsigned int mistrustCount_; - - /* Number of frames that need to be dropped on startup. */ - unsigned int dropFrameCount_; - - /* Frame timestamp for the last run of the controller. */ - uint64_t lastRunTimestamp_; - - /* Do we run a Controller::process() for this frame? */ - bool processPending_; - - /* LS table allocation passed in from the pipeline handler. */ - SharedFD lsTableHandle_; - void *lsTable_; - - /* Distinguish the first camera start from others. */ - bool firstStart_; - - /* Frame duration (1/fps) limits. */ - Duration minFrameDuration_; - Duration maxFrameDuration_; - - /* Track the frame length times over FrameLengthsQueueSize frames. */ - std::deque frameLengths_; - Duration lastTimeout_; -}; - -int IPARPi::init(const IPASettings &settings, bool lensPresent, IPAInitResult *result) -{ - /* - * Load the "helper" for this sensor. This tells us all the device specific stuff - * that the kernel driver doesn't. We only do this the first time; we don't need - * to re-parse the metadata after a simple mode-switch for no reason. - */ - helper_ = std::unique_ptr(RPiController::CamHelper::create(settings.sensorModel)); - if (!helper_) { - LOG(IPARPI, Error) << "Could not create camera helper for " - << settings.sensorModel; - return -EINVAL; - } - - /* - * Pass out the sensor config to the pipeline handler in order - * to setup the staggered writer class. - */ - int gainDelay, exposureDelay, vblankDelay, hblankDelay, sensorMetadata; - helper_->getDelays(exposureDelay, gainDelay, vblankDelay, hblankDelay); - sensorMetadata = helper_->sensorEmbeddedDataPresent(); - - result->sensorConfig.gainDelay = gainDelay; - result->sensorConfig.exposureDelay = exposureDelay; - result->sensorConfig.vblankDelay = vblankDelay; - result->sensorConfig.hblankDelay = hblankDelay; - result->sensorConfig.sensorMetadata = sensorMetadata; - - /* Load the tuning file for this sensor. */ - int ret = controller_.read(settings.configurationFile.c_str()); - if (ret) { - LOG(IPARPI, Error) - << "Failed to load tuning data file " - << settings.configurationFile; - return ret; - } - - const std::string &target = controller_.getTarget(); - if (target != "bcm2835") { - LOG(IPARPI, Error) - << "Tuning data file target returned \"" << target << "\"" - << ", expected \"bcm2835\""; - return -EINVAL; - } - - lensPresent_ = lensPresent; - - controller_.initialise(); - - /* Return the controls handled by the IPA */ - ControlInfoMap::Map ctrlMap = ipaControls; - if (lensPresent_) - ctrlMap.merge(ControlInfoMap::Map(ipaAfControls)); - result->controlInfo = ControlInfoMap(std::move(ctrlMap), controls::controls); - - return 0; -} - -void IPARPi::start(const ControlList &controls, StartConfig *startConfig) -{ - RPiController::Metadata metadata; - - ASSERT(startConfig); - if (!controls.empty()) { - /* We have been given some controls to action before start. */ - queueRequest(controls); - } - - controller_.switchMode(mode_, &metadata); - - /* Reset the frame lengths queue state. */ - lastTimeout_ = 0s; - frameLengths_.clear(); - frameLengths_.resize(FrameLengthsQueueSize, 0s); - - /* SwitchMode may supply updated exposure/gain values to use. */ - AgcStatus agcStatus; - agcStatus.shutterTime = 0.0s; - agcStatus.analogueGain = 0.0; - - metadata.get("agc.status", agcStatus); - if (agcStatus.shutterTime && agcStatus.analogueGain) { - ControlList ctrls(sensorCtrls_); - applyAGC(&agcStatus, ctrls); - startConfig->controls = std::move(ctrls); - setCameraTimeoutValue(); - } - - /* - * Initialise frame counts, and decide how many frames must be hidden or - * "mistrusted", which depends on whether this is a startup from cold, - * or merely a mode switch in a running system. - */ - frameCount_ = 0; - checkCount_ = 0; - if (firstStart_) { - dropFrameCount_ = helper_->hideFramesStartup(); - mistrustCount_ = helper_->mistrustFramesStartup(); - - /* - * Query the AGC/AWB for how many frames they may take to - * converge sufficiently. Where these numbers are non-zero - * we must allow for the frames with bad statistics - * (mistrustCount_) that they won't see. But if zero (i.e. - * no convergence necessary), no frames need to be dropped. - */ - unsigned int agcConvergenceFrames = 0; - RPiController::AgcAlgorithm *agc = dynamic_cast( - controller_.getAlgorithm("agc")); - if (agc) { - agcConvergenceFrames = agc->getConvergenceFrames(); - if (agcConvergenceFrames) - agcConvergenceFrames += mistrustCount_; - } - - unsigned int awbConvergenceFrames = 0; - RPiController::AwbAlgorithm *awb = dynamic_cast( - controller_.getAlgorithm("awb")); - if (awb) { - awbConvergenceFrames = awb->getConvergenceFrames(); - if (awbConvergenceFrames) - awbConvergenceFrames += mistrustCount_; - } - - dropFrameCount_ = std::max({ dropFrameCount_, agcConvergenceFrames, awbConvergenceFrames }); - LOG(IPARPI, Debug) << "Drop " << dropFrameCount_ << " frames on startup"; - } else { - dropFrameCount_ = helper_->hideFramesModeSwitch(); - mistrustCount_ = helper_->mistrustFramesModeSwitch(); - } - - startConfig->dropFrameCount = dropFrameCount_; - - firstStart_ = false; - lastRunTimestamp_ = 0; -} - -void IPARPi::setMode(const IPACameraSensorInfo &sensorInfo) -{ - mode_.bitdepth = sensorInfo.bitsPerPixel; - mode_.width = sensorInfo.outputSize.width; - mode_.height = sensorInfo.outputSize.height; - mode_.sensorWidth = sensorInfo.activeAreaSize.width; - mode_.sensorHeight = sensorInfo.activeAreaSize.height; - mode_.cropX = sensorInfo.analogCrop.x; - mode_.cropY = sensorInfo.analogCrop.y; - mode_.pixelRate = sensorInfo.pixelRate; - - /* - * Calculate scaling parameters. The scale_[xy] factors are determined - * by the ratio between the crop rectangle size and the output size. - */ - mode_.scaleX = sensorInfo.analogCrop.width / sensorInfo.outputSize.width; - mode_.scaleY = sensorInfo.analogCrop.height / sensorInfo.outputSize.height; - - /* - * We're not told by the pipeline handler how scaling is split between - * binning and digital scaling. For now, as a heuristic, assume that - * downscaling up to 2 is achieved through binning, and that any - * additional scaling is achieved through digital scaling. - * - * \todo Get the pipeline handle to provide the full data - */ - mode_.binX = std::min(2, static_cast(mode_.scaleX)); - mode_.binY = std::min(2, static_cast(mode_.scaleY)); - - /* The noise factor is the square root of the total binning factor. */ - mode_.noiseFactor = sqrt(mode_.binX * mode_.binY); - - /* - * Calculate the line length as the ratio between the line length in - * pixels and the pixel rate. - */ - mode_.minLineLength = sensorInfo.minLineLength * (1.0s / sensorInfo.pixelRate); - mode_.maxLineLength = sensorInfo.maxLineLength * (1.0s / sensorInfo.pixelRate); - - /* - * Set the frame length limits for the mode to ensure exposure and - * framerate calculations are clipped appropriately. - */ - mode_.minFrameLength = sensorInfo.minFrameLength; - mode_.maxFrameLength = sensorInfo.maxFrameLength; - - /* Store these for convenience. */ - mode_.minFrameDuration = mode_.minFrameLength * mode_.minLineLength; - mode_.maxFrameDuration = mode_.maxFrameLength * mode_.maxLineLength; - - /* - * Some sensors may have different sensitivities in different modes; - * the CamHelper will know the correct value. - */ - mode_.sensitivity = helper_->getModeSensitivity(mode_); - - const ControlInfo &gainCtrl = sensorCtrls_.at(V4L2_CID_ANALOGUE_GAIN); - const ControlInfo &shutterCtrl = sensorCtrls_.at(V4L2_CID_EXPOSURE); - - mode_.minAnalogueGain = helper_->gain(gainCtrl.min().get()); - mode_.maxAnalogueGain = helper_->gain(gainCtrl.max().get()); - - /* Shutter speed is calculated based on the limits of the frame durations. */ - mode_.minShutter = helper_->exposure(shutterCtrl.min().get(), mode_.minLineLength); - mode_.maxShutter = Duration::max(); - helper_->getBlanking(mode_.maxShutter, - mode_.minFrameDuration, mode_.maxFrameDuration); -} - -int IPARPi::configure(const IPACameraSensorInfo &sensorInfo, const IPAConfig &ipaConfig, - ControlList *controls, IPAConfigResult *result) -{ - sensorCtrls_ = ipaConfig.sensorControls; - ispCtrls_ = ipaConfig.ispControls; - - if (!validateSensorControls()) { - LOG(IPARPI, Error) << "Sensor control validation failed."; - return -1; - } - - if (!validateIspControls()) { - LOG(IPARPI, Error) << "ISP control validation failed."; - return -1; - } - - if (lensPresent_) { - lensCtrls_ = ipaConfig.lensControls; - if (!validateLensControls()) { - LOG(IPARPI, Warning) << "Lens validation failed, " - << "no lens control will be available."; - lensPresent_ = false; - } - } - - /* Setup a metadata ControlList to output metadata. */ - libcameraMetadata_ = ControlList(controls::controls); - - /* Re-assemble camera mode using the sensor info. */ - setMode(sensorInfo); - - mode_.transform = static_cast(ipaConfig.transform); - - /* Store the lens shading table pointer and handle if available. */ - if (ipaConfig.lsTableHandle.isValid()) { - /* Remove any previous table, if there was one. */ - if (lsTable_) { - munmap(lsTable_, MaxLsGridSize); - lsTable_ = nullptr; - } - - /* Map the LS table buffer into user space. */ - lsTableHandle_ = std::move(ipaConfig.lsTableHandle); - if (lsTableHandle_.isValid()) { - lsTable_ = mmap(nullptr, MaxLsGridSize, PROT_READ | PROT_WRITE, - MAP_SHARED, lsTableHandle_.get(), 0); - - if (lsTable_ == MAP_FAILED) { - LOG(IPARPI, Error) << "dmaHeap mmap failure for LS table."; - lsTable_ = nullptr; - } - } - } - - /* Pass the camera mode to the CamHelper to setup algorithms. */ - helper_->setCameraMode(mode_); - - /* - * Initialise this ControlList correctly, even if empty, in case the IPA is - * running is isolation mode (passing the ControlList through the IPC layer). - */ - ControlList ctrls(sensorCtrls_); - - /* The pipeline handler passes out the mode's sensitivity. */ - result->modeSensitivity = mode_.sensitivity; - - if (firstStart_) { - /* Supply initial values for frame durations. */ - applyFrameDurations(defaultMinFrameDuration, defaultMaxFrameDuration); - - /* Supply initial values for gain and exposure. */ - AgcStatus agcStatus; - agcStatus.shutterTime = defaultExposureTime; - agcStatus.analogueGain = defaultAnalogueGain; - applyAGC(&agcStatus, ctrls); - } - - ASSERT(controls); - *controls = std::move(ctrls); - - /* - * Apply the correct limits to the exposure, gain and frame duration controls - * based on the current sensor mode. - */ - ControlInfoMap::Map ctrlMap = ipaControls; - ctrlMap[&controls::FrameDurationLimits] = - ControlInfo(static_cast(mode_.minFrameDuration.get()), - static_cast(mode_.maxFrameDuration.get())); - - ctrlMap[&controls::AnalogueGain] = - ControlInfo(static_cast(mode_.minAnalogueGain), - static_cast(mode_.maxAnalogueGain)); - - ctrlMap[&controls::ExposureTime] = - ControlInfo(static_cast(mode_.minShutter.get()), - static_cast(mode_.maxShutter.get())); - - /* Declare Autofocus controls, only if we have a controllable lens */ - if (lensPresent_) - ctrlMap.merge(ControlInfoMap::Map(ipaAfControls)); - - result->controlInfo = ControlInfoMap(std::move(ctrlMap), controls::controls); - return 0; -} - -void IPARPi::mapBuffers(const std::vector &buffers) -{ - for (const IPABuffer &buffer : buffers) { - const FrameBuffer fb(buffer.planes); - buffers_.emplace(buffer.id, - MappedFrameBuffer(&fb, MappedFrameBuffer::MapFlag::ReadWrite)); - } -} - -void IPARPi::unmapBuffers(const std::vector &ids) -{ - for (unsigned int id : ids) { - auto it = buffers_.find(id); - if (it == buffers_.end()) - continue; - - buffers_.erase(id); - } -} - -void IPARPi::signalStatReady(uint32_t bufferId, uint32_t ipaContext) -{ - unsigned int context = ipaContext % rpiMetadata_.size(); - - if (++checkCount_ != frameCount_) /* assert here? */ - LOG(IPARPI, Error) << "WARNING: Prepare/Process mismatch!!!"; - if (processPending_ && frameCount_ > mistrustCount_) - processStats(bufferId, context); - - reportMetadata(context); - - statsMetadataComplete.emit(bufferId, libcameraMetadata_); -} - -void IPARPi::signalQueueRequest(const ControlList &controls) -{ - queueRequest(controls); -} - -void IPARPi::signalIspPrepare(const ISPConfig &data) -{ - /* - * At start-up, or after a mode-switch, we may want to - * avoid running the control algos for a few frames in case - * they are "unreliable". - */ - prepareISP(data); - frameCount_++; - - /* Ready to push the input buffer into the ISP. */ - runIsp.emit(data.bayerBufferId); -} - -void IPARPi::reportMetadata(unsigned int ipaContext) -{ - RPiController::Metadata &rpiMetadata = rpiMetadata_[ipaContext]; - std::unique_lock lock(rpiMetadata); - - /* - * Certain information about the current frame and how it will be - * processed can be extracted and placed into the libcamera metadata - * buffer, where an application could query it. - */ - DeviceStatus *deviceStatus = rpiMetadata.getLocked("device.status"); - if (deviceStatus) { - libcameraMetadata_.set(controls::ExposureTime, - deviceStatus->shutterSpeed.get()); - libcameraMetadata_.set(controls::AnalogueGain, deviceStatus->analogueGain); - libcameraMetadata_.set(controls::FrameDuration, - helper_->exposure(deviceStatus->frameLength, deviceStatus->lineLength).get()); - if (deviceStatus->sensorTemperature) - libcameraMetadata_.set(controls::SensorTemperature, *deviceStatus->sensorTemperature); - if (deviceStatus->lensPosition) - libcameraMetadata_.set(controls::LensPosition, *deviceStatus->lensPosition); - } - - AgcStatus *agcStatus = rpiMetadata.getLocked("agc.status"); - if (agcStatus) { - libcameraMetadata_.set(controls::AeLocked, agcStatus->locked); - libcameraMetadata_.set(controls::DigitalGain, agcStatus->digitalGain); - } - - LuxStatus *luxStatus = rpiMetadata.getLocked("lux.status"); - if (luxStatus) - libcameraMetadata_.set(controls::Lux, luxStatus->lux); - - AwbStatus *awbStatus = rpiMetadata.getLocked("awb.status"); - if (awbStatus) { - libcameraMetadata_.set(controls::ColourGains, { static_cast(awbStatus->gainR), - static_cast(awbStatus->gainB) }); - libcameraMetadata_.set(controls::ColourTemperature, awbStatus->temperatureK); - } - - BlackLevelStatus *blackLevelStatus = rpiMetadata.getLocked("black_level.status"); - if (blackLevelStatus) - libcameraMetadata_.set(controls::SensorBlackLevels, - { static_cast(blackLevelStatus->blackLevelR), - static_cast(blackLevelStatus->blackLevelG), - static_cast(blackLevelStatus->blackLevelG), - static_cast(blackLevelStatus->blackLevelB) }); - - RPiController::FocusRegions *focusStatus = - rpiMetadata.getLocked("focus.status"); - if (focusStatus) { - /* - * Calculate the average FoM over the central (symmetric) positions - * to give an overall scene FoM. This can change later if it is - * not deemed suitable. - */ - libcamera::Size size = focusStatus->size(); - unsigned rows = size.height; - unsigned cols = size.width; - - uint64_t sum = 0; - unsigned int numRegions = 0; - for (unsigned r = rows / 3; r < rows - rows / 3; ++r) { - for (unsigned c = cols / 4; c < cols - cols / 4; ++c) { - sum += focusStatus->get({ (int)c, (int)r }).val; - numRegions++; - } - } - - uint32_t focusFoM = (sum / numRegions) >> 16; - libcameraMetadata_.set(controls::FocusFoM, focusFoM); - } - - CcmStatus *ccmStatus = rpiMetadata.getLocked("ccm.status"); - if (ccmStatus) { - float m[9]; - for (unsigned int i = 0; i < 9; i++) - m[i] = ccmStatus->matrix[i]; - libcameraMetadata_.set(controls::ColourCorrectionMatrix, m); - } - - const AfStatus *afStatus = rpiMetadata.getLocked("af.status"); - if (afStatus) { - int32_t s, p; - switch (afStatus->state) { - case AfState::Scanning: - s = controls::AfStateScanning; - break; - case AfState::Focused: - s = controls::AfStateFocused; - break; - case AfState::Failed: - s = controls::AfStateFailed; - break; - default: - s = controls::AfStateIdle; - } - switch (afStatus->pauseState) { - case AfPauseState::Pausing: - p = controls::AfPauseStatePausing; - break; - case AfPauseState::Paused: - p = controls::AfPauseStatePaused; - break; - default: - p = controls::AfPauseStateRunning; - } - libcameraMetadata_.set(controls::AfState, s); - libcameraMetadata_.set(controls::AfPauseState, p); - } -} - -bool IPARPi::validateSensorControls() -{ - static const uint32_t ctrls[] = { - V4L2_CID_ANALOGUE_GAIN, - V4L2_CID_EXPOSURE, - V4L2_CID_VBLANK, - V4L2_CID_HBLANK, - }; - - for (auto c : ctrls) { - if (sensorCtrls_.find(c) == sensorCtrls_.end()) { - LOG(IPARPI, Error) << "Unable to find sensor control " - << utils::hex(c); - return false; - } - } - - return true; -} - -bool IPARPi::validateIspControls() -{ - static const uint32_t ctrls[] = { - V4L2_CID_RED_BALANCE, - V4L2_CID_BLUE_BALANCE, - V4L2_CID_DIGITAL_GAIN, - V4L2_CID_USER_BCM2835_ISP_CC_MATRIX, - V4L2_CID_USER_BCM2835_ISP_GAMMA, - V4L2_CID_USER_BCM2835_ISP_BLACK_LEVEL, - V4L2_CID_USER_BCM2835_ISP_GEQ, - V4L2_CID_USER_BCM2835_ISP_DENOISE, - V4L2_CID_USER_BCM2835_ISP_SHARPEN, - V4L2_CID_USER_BCM2835_ISP_DPC, - V4L2_CID_USER_BCM2835_ISP_LENS_SHADING, - V4L2_CID_USER_BCM2835_ISP_CDN, - }; - - for (auto c : ctrls) { - if (ispCtrls_.find(c) == ispCtrls_.end()) { - LOG(IPARPI, Error) << "Unable to find ISP control " - << utils::hex(c); - return false; - } - } - - return true; -} - -bool IPARPi::validateLensControls() -{ - if (lensCtrls_.find(V4L2_CID_FOCUS_ABSOLUTE) == lensCtrls_.end()) { - LOG(IPARPI, Error) << "Unable to find Lens control V4L2_CID_FOCUS_ABSOLUTE"; - return false; - } - - return true; -} - -/* - * Converting between enums (used in the libcamera API) and the names that - * we use to identify different modes. Unfortunately, the conversion tables - * must be kept up-to-date by hand. - */ -static const std::map MeteringModeTable = { - { controls::MeteringCentreWeighted, "centre-weighted" }, - { controls::MeteringSpot, "spot" }, - { controls::MeteringMatrix, "matrix" }, - { controls::MeteringCustom, "custom" }, -}; - -static const std::map ConstraintModeTable = { - { controls::ConstraintNormal, "normal" }, - { controls::ConstraintHighlight, "highlight" }, - { controls::ConstraintShadows, "shadows" }, - { controls::ConstraintCustom, "custom" }, -}; - -static const std::map ExposureModeTable = { - { controls::ExposureNormal, "normal" }, - { controls::ExposureShort, "short" }, - { controls::ExposureLong, "long" }, - { controls::ExposureCustom, "custom" }, -}; - -static const std::map AwbModeTable = { - { controls::AwbAuto, "auto" }, - { controls::AwbIncandescent, "incandescent" }, - { controls::AwbTungsten, "tungsten" }, - { controls::AwbFluorescent, "fluorescent" }, - { controls::AwbIndoor, "indoor" }, - { controls::AwbDaylight, "daylight" }, - { controls::AwbCloudy, "cloudy" }, - { controls::AwbCustom, "custom" }, -}; - -static const std::map DenoiseModeTable = { - { controls::draft::NoiseReductionModeOff, RPiController::DenoiseMode::Off }, - { controls::draft::NoiseReductionModeFast, RPiController::DenoiseMode::ColourFast }, - { controls::draft::NoiseReductionModeHighQuality, RPiController::DenoiseMode::ColourHighQuality }, - { controls::draft::NoiseReductionModeMinimal, RPiController::DenoiseMode::ColourOff }, - { controls::draft::NoiseReductionModeZSL, RPiController::DenoiseMode::ColourHighQuality }, -}; - -static const std::map AfModeTable = { - { controls::AfModeManual, RPiController::AfAlgorithm::AfModeManual }, - { controls::AfModeAuto, RPiController::AfAlgorithm::AfModeAuto }, - { controls::AfModeContinuous, RPiController::AfAlgorithm::AfModeContinuous }, -}; - -static const std::map AfRangeTable = { - { controls::AfRangeNormal, RPiController::AfAlgorithm::AfRangeNormal }, - { controls::AfRangeMacro, RPiController::AfAlgorithm::AfRangeMacro }, - { controls::AfRangeFull, RPiController::AfAlgorithm::AfRangeFull }, -}; - -static const std::map AfPauseTable = { - { controls::AfPauseImmediate, RPiController::AfAlgorithm::AfPauseImmediate }, - { controls::AfPauseDeferred, RPiController::AfAlgorithm::AfPauseDeferred }, - { controls::AfPauseResume, RPiController::AfAlgorithm::AfPauseResume }, -}; - -void IPARPi::queueRequest(const ControlList &controls) -{ - using RPiController::AfAlgorithm; - - /* Clear the return metadata buffer. */ - libcameraMetadata_.clear(); - - /* Because some AF controls are mode-specific, handle AF mode change first. */ - if (controls.contains(controls::AF_MODE)) { - AfAlgorithm *af = dynamic_cast(controller_.getAlgorithm("af")); - if (!af) { - LOG(IPARPI, Warning) - << "Could not set AF_MODE - no AF algorithm"; - } - - int32_t idx = controls.get(controls::AF_MODE).get(); - auto mode = AfModeTable.find(idx); - if (mode == AfModeTable.end()) { - LOG(IPARPI, Error) << "AF mode " << idx - << " not recognised"; - } else - af->setMode(mode->second); - } - - /* Iterate over controls */ - for (auto const &ctrl : controls) { - LOG(IPARPI, Debug) << "Request ctrl: " - << controls::controls.at(ctrl.first)->name() - << " = " << ctrl.second.toString(); - - switch (ctrl.first) { - case controls::AE_ENABLE: { - RPiController::AgcAlgorithm *agc = dynamic_cast( - controller_.getAlgorithm("agc")); - if (!agc) { - LOG(IPARPI, Warning) - << "Could not set AE_ENABLE - no AGC algorithm"; - break; - } - - if (ctrl.second.get() == false) - agc->disableAuto(); - else - agc->enableAuto(); - - libcameraMetadata_.set(controls::AeEnable, ctrl.second.get()); - break; - } - - case controls::EXPOSURE_TIME: { - RPiController::AgcAlgorithm *agc = dynamic_cast( - controller_.getAlgorithm("agc")); - if (!agc) { - LOG(IPARPI, Warning) - << "Could not set EXPOSURE_TIME - no AGC algorithm"; - break; - } - - /* The control provides units of microseconds. */ - agc->setFixedShutter(ctrl.second.get() * 1.0us); - - libcameraMetadata_.set(controls::ExposureTime, ctrl.second.get()); - break; - } - - case controls::ANALOGUE_GAIN: { - RPiController::AgcAlgorithm *agc = dynamic_cast( - controller_.getAlgorithm("agc")); - if (!agc) { - LOG(IPARPI, Warning) - << "Could not set ANALOGUE_GAIN - no AGC algorithm"; - break; - } - - agc->setFixedAnalogueGain(ctrl.second.get()); - - libcameraMetadata_.set(controls::AnalogueGain, - ctrl.second.get()); - break; - } - - case controls::AE_METERING_MODE: { - RPiController::AgcAlgorithm *agc = dynamic_cast( - controller_.getAlgorithm("agc")); - if (!agc) { - LOG(IPARPI, Warning) - << "Could not set AE_METERING_MODE - no AGC algorithm"; - break; - } - - int32_t idx = ctrl.second.get(); - if (MeteringModeTable.count(idx)) { - agc->setMeteringMode(MeteringModeTable.at(idx)); - libcameraMetadata_.set(controls::AeMeteringMode, idx); - } else { - LOG(IPARPI, Error) << "Metering mode " << idx - << " not recognised"; - } - break; - } - - case controls::AE_CONSTRAINT_MODE: { - RPiController::AgcAlgorithm *agc = dynamic_cast( - controller_.getAlgorithm("agc")); - if (!agc) { - LOG(IPARPI, Warning) - << "Could not set AE_CONSTRAINT_MODE - no AGC algorithm"; - break; - } - - int32_t idx = ctrl.second.get(); - if (ConstraintModeTable.count(idx)) { - agc->setConstraintMode(ConstraintModeTable.at(idx)); - libcameraMetadata_.set(controls::AeConstraintMode, idx); - } else { - LOG(IPARPI, Error) << "Constraint mode " << idx - << " not recognised"; - } - break; - } - - case controls::AE_EXPOSURE_MODE: { - RPiController::AgcAlgorithm *agc = dynamic_cast( - controller_.getAlgorithm("agc")); - if (!agc) { - LOG(IPARPI, Warning) - << "Could not set AE_EXPOSURE_MODE - no AGC algorithm"; - break; - } - - int32_t idx = ctrl.second.get(); - if (ExposureModeTable.count(idx)) { - agc->setExposureMode(ExposureModeTable.at(idx)); - libcameraMetadata_.set(controls::AeExposureMode, idx); - } else { - LOG(IPARPI, Error) << "Exposure mode " << idx - << " not recognised"; - } - break; - } - - case controls::EXPOSURE_VALUE: { - RPiController::AgcAlgorithm *agc = dynamic_cast( - controller_.getAlgorithm("agc")); - if (!agc) { - LOG(IPARPI, Warning) - << "Could not set EXPOSURE_VALUE - no AGC algorithm"; - break; - } - - /* - * The SetEv() function takes in a direct exposure multiplier. - * So convert to 2^EV - */ - double ev = pow(2.0, ctrl.second.get()); - agc->setEv(ev); - libcameraMetadata_.set(controls::ExposureValue, - ctrl.second.get()); - break; - } - - case controls::AWB_ENABLE: { - RPiController::AwbAlgorithm *awb = dynamic_cast( - controller_.getAlgorithm("awb")); - if (!awb) { - LOG(IPARPI, Warning) - << "Could not set AWB_ENABLE - no AWB algorithm"; - break; - } - - if (ctrl.second.get() == false) - awb->disableAuto(); - else - awb->enableAuto(); - - libcameraMetadata_.set(controls::AwbEnable, - ctrl.second.get()); - break; - } - - case controls::AWB_MODE: { - RPiController::AwbAlgorithm *awb = dynamic_cast( - controller_.getAlgorithm("awb")); - if (!awb) { - LOG(IPARPI, Warning) - << "Could not set AWB_MODE - no AWB algorithm"; - break; - } - - int32_t idx = ctrl.second.get(); - if (AwbModeTable.count(idx)) { - awb->setMode(AwbModeTable.at(idx)); - libcameraMetadata_.set(controls::AwbMode, idx); - } else { - LOG(IPARPI, Error) << "AWB mode " << idx - << " not recognised"; - } - break; - } - - case controls::COLOUR_GAINS: { - auto gains = ctrl.second.get>(); - RPiController::AwbAlgorithm *awb = dynamic_cast( - controller_.getAlgorithm("awb")); - if (!awb) { - LOG(IPARPI, Warning) - << "Could not set COLOUR_GAINS - no AWB algorithm"; - break; - } - - awb->setManualGains(gains[0], gains[1]); - if (gains[0] != 0.0f && gains[1] != 0.0f) - /* A gain of 0.0f will switch back to auto mode. */ - libcameraMetadata_.set(controls::ColourGains, - { gains[0], gains[1] }); - break; - } - - case controls::BRIGHTNESS: { - RPiController::ContrastAlgorithm *contrast = dynamic_cast( - controller_.getAlgorithm("contrast")); - if (!contrast) { - LOG(IPARPI, Warning) - << "Could not set BRIGHTNESS - no contrast algorithm"; - break; - } - - contrast->setBrightness(ctrl.second.get() * 65536); - libcameraMetadata_.set(controls::Brightness, - ctrl.second.get()); - break; - } - - case controls::CONTRAST: { - RPiController::ContrastAlgorithm *contrast = dynamic_cast( - controller_.getAlgorithm("contrast")); - if (!contrast) { - LOG(IPARPI, Warning) - << "Could not set CONTRAST - no contrast algorithm"; - break; - } - - contrast->setContrast(ctrl.second.get()); - libcameraMetadata_.set(controls::Contrast, - ctrl.second.get()); - break; - } - - case controls::SATURATION: { - RPiController::CcmAlgorithm *ccm = dynamic_cast( - controller_.getAlgorithm("ccm")); - if (!ccm) { - LOG(IPARPI, Warning) - << "Could not set SATURATION - no ccm algorithm"; - break; - } - - ccm->setSaturation(ctrl.second.get()); - libcameraMetadata_.set(controls::Saturation, - ctrl.second.get()); - break; - } - - case controls::SHARPNESS: { - RPiController::SharpenAlgorithm *sharpen = dynamic_cast( - controller_.getAlgorithm("sharpen")); - if (!sharpen) { - LOG(IPARPI, Warning) - << "Could not set SHARPNESS - no sharpen algorithm"; - break; - } - - sharpen->setStrength(ctrl.second.get()); - libcameraMetadata_.set(controls::Sharpness, - ctrl.second.get()); - break; - } - - case controls::SCALER_CROP: { - /* We do nothing with this, but should avoid the warning below. */ - break; - } - - case controls::FRAME_DURATION_LIMITS: { - auto frameDurations = ctrl.second.get>(); - applyFrameDurations(frameDurations[0] * 1.0us, frameDurations[1] * 1.0us); - break; - } - - case controls::NOISE_REDUCTION_MODE: { - RPiController::DenoiseAlgorithm *sdn = dynamic_cast( - controller_.getAlgorithm("SDN")); - if (!sdn) { - LOG(IPARPI, Warning) - << "Could not set NOISE_REDUCTION_MODE - no SDN algorithm"; - break; - } - - int32_t idx = ctrl.second.get(); - auto mode = DenoiseModeTable.find(idx); - if (mode != DenoiseModeTable.end()) { - sdn->setMode(mode->second); - - /* - * \todo If the colour denoise is not going to run due to an - * analysis image resolution or format mismatch, we should - * report the status correctly in the metadata. - */ - libcameraMetadata_.set(controls::draft::NoiseReductionMode, idx); - } else { - LOG(IPARPI, Error) << "Noise reduction mode " << idx - << " not recognised"; - } - break; - } - - case controls::AF_MODE: - break; /* We already handled this one above */ - - case controls::AF_RANGE: { - AfAlgorithm *af = dynamic_cast(controller_.getAlgorithm("af")); - if (!af) { - LOG(IPARPI, Warning) - << "Could not set AF_RANGE - no focus algorithm"; - break; - } - - auto range = AfRangeTable.find(ctrl.second.get()); - if (range == AfRangeTable.end()) { - LOG(IPARPI, Error) << "AF range " << ctrl.second.get() - << " not recognised"; - break; - } - af->setRange(range->second); - break; - } - - case controls::AF_SPEED: { - AfAlgorithm *af = dynamic_cast(controller_.getAlgorithm("af")); - if (!af) { - LOG(IPARPI, Warning) - << "Could not set AF_SPEED - no focus algorithm"; - break; - } - - AfAlgorithm::AfSpeed speed = ctrl.second.get() == controls::AfSpeedFast ? - AfAlgorithm::AfSpeedFast : AfAlgorithm::AfSpeedNormal; - af->setSpeed(speed); - break; - } - - case controls::AF_METERING: { - AfAlgorithm *af = dynamic_cast(controller_.getAlgorithm("af")); - if (!af) { - LOG(IPARPI, Warning) - << "Could not set AF_METERING - no AF algorithm"; - break; - } - af->setMetering(ctrl.second.get() == controls::AfMeteringWindows); - break; - } - - case controls::AF_WINDOWS: { - AfAlgorithm *af = dynamic_cast(controller_.getAlgorithm("af")); - if (!af) { - LOG(IPARPI, Warning) - << "Could not set AF_WINDOWS - no AF algorithm"; - break; - } - af->setWindows(ctrl.second.get>()); - break; - } - - case controls::AF_PAUSE: { - AfAlgorithm *af = dynamic_cast(controller_.getAlgorithm("af")); - if (!af || af->getMode() != AfAlgorithm::AfModeContinuous) { - LOG(IPARPI, Warning) - << "Could not set AF_PAUSE - no AF algorithm or not Continuous"; - break; - } - auto pause = AfPauseTable.find(ctrl.second.get()); - if (pause == AfPauseTable.end()) { - LOG(IPARPI, Error) << "AF pause " << ctrl.second.get() - << " not recognised"; - break; - } - af->pause(pause->second); - break; - } - - case controls::AF_TRIGGER: { - AfAlgorithm *af = dynamic_cast(controller_.getAlgorithm("af")); - if (!af || af->getMode() != AfAlgorithm::AfModeAuto) { - LOG(IPARPI, Warning) - << "Could not set AF_TRIGGER - no AF algorithm or not Auto"; - break; - } else { - if (ctrl.second.get() == controls::AfTriggerStart) - af->triggerScan(); - else - af->cancelScan(); - } - break; - } - - case controls::LENS_POSITION: { - AfAlgorithm *af = dynamic_cast(controller_.getAlgorithm("af")); - if (af) { - int32_t hwpos; - if (af->setLensPosition(ctrl.second.get(), &hwpos)) { - ControlList lensCtrls(lensCtrls_); - lensCtrls.set(V4L2_CID_FOCUS_ABSOLUTE, hwpos); - setLensControls.emit(lensCtrls); - } - } else { - LOG(IPARPI, Warning) - << "Could not set LENS_POSITION - no AF algorithm"; - } - break; - } - - default: - LOG(IPARPI, Warning) - << "Ctrl " << controls::controls.at(ctrl.first)->name() - << " is not handled."; - break; - } - } -} - -void IPARPi::returnEmbeddedBuffer(unsigned int bufferId) -{ - embeddedComplete.emit(bufferId); -} - -void IPARPi::prepareISP(const ISPConfig &data) -{ - int64_t frameTimestamp = data.controls.get(controls::SensorTimestamp).value_or(0); - unsigned int ipaContext = data.ipaContext % rpiMetadata_.size(); - RPiController::Metadata &rpiMetadata = rpiMetadata_[ipaContext]; - Span embeddedBuffer; - - rpiMetadata.clear(); - fillDeviceStatus(data.controls, ipaContext); - - if (data.embeddedBufferPresent) { - /* - * Pipeline handler has supplied us with an embedded data buffer, - * we must pass it to the CamHelper for parsing. - */ - auto it = buffers_.find(data.embeddedBufferId); - ASSERT(it != buffers_.end()); - embeddedBuffer = it->second.planes()[0]; - } - - /* - * AGC wants to know the algorithm status from the time it actioned the - * sensor exposure/gain changes. So fetch it from the metadata list - * indexed by the IPA cookie returned, and put it in the current frame - * metadata. - */ - AgcStatus agcStatus; - RPiController::Metadata &delayedMetadata = rpiMetadata_[data.delayContext]; - if (!delayedMetadata.get("agc.status", agcStatus)) - rpiMetadata.set("agc.delayed_status", agcStatus); - - /* - * This may overwrite the DeviceStatus using values from the sensor - * metadata, and may also do additional custom processing. - */ - helper_->prepare(embeddedBuffer, rpiMetadata); - - /* Done with embedded data now, return to pipeline handler asap. */ - if (data.embeddedBufferPresent) - returnEmbeddedBuffer(data.embeddedBufferId); - - /* Allow a 10% margin on the comparison below. */ - Duration delta = (frameTimestamp - lastRunTimestamp_) * 1.0ns; - if (lastRunTimestamp_ && frameCount_ > dropFrameCount_ && - delta < controllerMinFrameDuration * 0.9) { - /* - * Ensure we merge the previous frame's metadata with the current - * frame. This will not overwrite exposure/gain values for the - * current frame, or any other bits of metadata that were added - * in helper_->Prepare(). - */ - RPiController::Metadata &lastMetadata = - rpiMetadata_[(ipaContext ? ipaContext : rpiMetadata_.size()) - 1]; - rpiMetadata.mergeCopy(lastMetadata); - processPending_ = false; - return; - } - - lastRunTimestamp_ = frameTimestamp; - processPending_ = true; - - ControlList ctrls(ispCtrls_); - - controller_.prepare(&rpiMetadata); - - /* Lock the metadata buffer to avoid constant locks/unlocks. */ - std::unique_lock lock(rpiMetadata); - - AwbStatus *awbStatus = rpiMetadata.getLocked("awb.status"); - if (awbStatus) - applyAWB(awbStatus, ctrls); - - CcmStatus *ccmStatus = rpiMetadata.getLocked("ccm.status"); - if (ccmStatus) - applyCCM(ccmStatus, ctrls); - - AgcStatus *dgStatus = rpiMetadata.getLocked("agc.status"); - if (dgStatus) - applyDG(dgStatus, ctrls); - - AlscStatus *lsStatus = rpiMetadata.getLocked("alsc.status"); - if (lsStatus) - applyLS(lsStatus, ctrls); - - ContrastStatus *contrastStatus = rpiMetadata.getLocked("contrast.status"); - if (contrastStatus) - applyGamma(contrastStatus, ctrls); - - BlackLevelStatus *blackLevelStatus = rpiMetadata.getLocked("black_level.status"); - if (blackLevelStatus) - applyBlackLevel(blackLevelStatus, ctrls); - - GeqStatus *geqStatus = rpiMetadata.getLocked("geq.status"); - if (geqStatus) - applyGEQ(geqStatus, ctrls); - - DenoiseStatus *denoiseStatus = rpiMetadata.getLocked("denoise.status"); - if (denoiseStatus) - applyDenoise(denoiseStatus, ctrls); - - SharpenStatus *sharpenStatus = rpiMetadata.getLocked("sharpen.status"); - if (sharpenStatus) - applySharpen(sharpenStatus, ctrls); - - DpcStatus *dpcStatus = rpiMetadata.getLocked("dpc.status"); - if (dpcStatus) - applyDPC(dpcStatus, ctrls); - - const AfStatus *afStatus = rpiMetadata.getLocked("af.status"); - if (afStatus) { - ControlList lensctrls(lensCtrls_); - applyAF(afStatus, lensctrls); - if (!lensctrls.empty()) - setLensControls.emit(lensctrls); - } - - if (!ctrls.empty()) - setIspControls.emit(ctrls); -} - -void IPARPi::fillDeviceStatus(const ControlList &sensorControls, unsigned int ipaContext) -{ - DeviceStatus deviceStatus = {}; - - int32_t exposureLines = sensorControls.get(V4L2_CID_EXPOSURE).get(); - int32_t gainCode = sensorControls.get(V4L2_CID_ANALOGUE_GAIN).get(); - int32_t vblank = sensorControls.get(V4L2_CID_VBLANK).get(); - int32_t hblank = sensorControls.get(V4L2_CID_HBLANK).get(); - - deviceStatus.lineLength = helper_->hblankToLineLength(hblank); - deviceStatus.shutterSpeed = helper_->exposure(exposureLines, deviceStatus.lineLength); - deviceStatus.analogueGain = helper_->gain(gainCode); - deviceStatus.frameLength = mode_.height + vblank; - - RPiController::AfAlgorithm *af = dynamic_cast( - controller_.getAlgorithm("af")); - if (af) - deviceStatus.lensPosition = af->getLensPosition(); - - LOG(IPARPI, Debug) << "Metadata - " << deviceStatus; - - rpiMetadata_[ipaContext].set("device.status", deviceStatus); -} - -RPiController::StatisticsPtr IPARPi::fillStatistics(bcm2835_isp_stats *stats) const -{ - using namespace RPiController; - - const Controller::HardwareConfig &hw = controller_.getHardwareConfig(); - unsigned int i; - StatisticsPtr statistics = - std::make_unique(Statistics::AgcStatsPos::PreWb, Statistics::ColourStatsPos::PostLsc); - - /* RGB histograms are not used, so do not populate them. */ - statistics->yHist = RPiController::Histogram(stats->hist[0].g_hist, - hw.numHistogramBins); - - /* All region sums are based on a 16-bit normalised pipeline bit-depth. */ - unsigned int scale = Statistics::NormalisationFactorPow2 - hw.pipelineWidth; - - statistics->awbRegions.init(hw.awbRegions); - for (i = 0; i < statistics->awbRegions.numRegions(); i++) - statistics->awbRegions.set(i, { { stats->awb_stats[i].r_sum << scale, - stats->awb_stats[i].g_sum << scale, - stats->awb_stats[i].b_sum << scale }, - stats->awb_stats[i].counted, - stats->awb_stats[i].notcounted }); - - statistics->agcRegions.init(hw.agcRegions); - for (i = 0; i < statistics->agcRegions.numRegions(); i++) - statistics->agcRegions.set(i, { { stats->agc_stats[i].r_sum << scale, - stats->agc_stats[i].g_sum << scale, - stats->agc_stats[i].b_sum << scale }, - stats->agc_stats[i].counted, - stats->awb_stats[i].notcounted }); - - statistics->focusRegions.init(hw.focusRegions); - for (i = 0; i < statistics->focusRegions.numRegions(); i++) - statistics->focusRegions.set(i, { stats->focus_stats[i].contrast_val[1][1] / 1000, - stats->focus_stats[i].contrast_val_num[1][1], - stats->focus_stats[i].contrast_val_num[1][0] }); - return statistics; -} - -void IPARPi::processStats(unsigned int bufferId, unsigned int ipaContext) -{ - RPiController::Metadata &rpiMetadata = rpiMetadata_[ipaContext]; - - auto it = buffers_.find(bufferId); - if (it == buffers_.end()) { - LOG(IPARPI, Error) << "Could not find stats buffer!"; - return; - } - - Span mem = it->second.planes()[0]; - bcm2835_isp_stats *stats = reinterpret_cast(mem.data()); - RPiController::StatisticsPtr statistics = fillStatistics(stats); - - /* Save the focus stats in the metadata structure to report out later. */ - rpiMetadata_[ipaContext].set("focus.status", statistics->focusRegions); - - helper_->process(statistics, rpiMetadata); - controller_.process(statistics, &rpiMetadata); - - struct AgcStatus agcStatus; - if (rpiMetadata.get("agc.status", agcStatus) == 0) { - ControlList ctrls(sensorCtrls_); - applyAGC(&agcStatus, ctrls); - - setDelayedControls.emit(ctrls, ipaContext); - setCameraTimeoutValue(); - } -} - -void IPARPi::setCameraTimeoutValue() -{ - /* - * Take the maximum value of the exposure queue as the camera timeout - * value to pass back to the pipeline handler. Only signal if it has changed - * from the last set value. - */ - auto max = std::max_element(frameLengths_.begin(), frameLengths_.end()); - - if (*max != lastTimeout_) { - setCameraTimeout.emit(max->get()); - lastTimeout_ = *max; - } -} - -void IPARPi::applyAWB(const struct AwbStatus *awbStatus, ControlList &ctrls) -{ - LOG(IPARPI, Debug) << "Applying WB R: " << awbStatus->gainR << " B: " - << awbStatus->gainB; - - ctrls.set(V4L2_CID_RED_BALANCE, - static_cast(awbStatus->gainR * 1000)); - ctrls.set(V4L2_CID_BLUE_BALANCE, - static_cast(awbStatus->gainB * 1000)); -} - -void IPARPi::applyFrameDurations(Duration minFrameDuration, Duration maxFrameDuration) -{ - /* - * This will only be applied once AGC recalculations occur. - * The values may be clamped based on the sensor mode capabilities as well. - */ - minFrameDuration_ = minFrameDuration ? minFrameDuration : defaultMinFrameDuration; - maxFrameDuration_ = maxFrameDuration ? maxFrameDuration : defaultMaxFrameDuration; - minFrameDuration_ = std::clamp(minFrameDuration_, - mode_.minFrameDuration, mode_.maxFrameDuration); - maxFrameDuration_ = std::clamp(maxFrameDuration_, - mode_.minFrameDuration, mode_.maxFrameDuration); - maxFrameDuration_ = std::max(maxFrameDuration_, minFrameDuration_); - - /* Return the validated limits via metadata. */ - libcameraMetadata_.set(controls::FrameDurationLimits, - { static_cast(minFrameDuration_.get()), - static_cast(maxFrameDuration_.get()) }); - - /* - * Calculate the maximum exposure time possible for the AGC to use. - * getBlanking() will update maxShutter with the largest exposure - * value possible. - */ - Duration maxShutter = Duration::max(); - helper_->getBlanking(maxShutter, minFrameDuration_, maxFrameDuration_); - - RPiController::AgcAlgorithm *agc = dynamic_cast( - controller_.getAlgorithm("agc")); - agc->setMaxShutter(maxShutter); -} - -void IPARPi::applyAGC(const struct AgcStatus *agcStatus, ControlList &ctrls) -{ - const int32_t minGainCode = helper_->gainCode(mode_.minAnalogueGain); - const int32_t maxGainCode = helper_->gainCode(mode_.maxAnalogueGain); - int32_t gainCode = helper_->gainCode(agcStatus->analogueGain); - - /* - * Ensure anything larger than the max gain code will not be passed to - * DelayedControls. The AGC will correctly handle a lower gain returned - * by the sensor, provided it knows the actual gain used. - */ - gainCode = std::clamp(gainCode, minGainCode, maxGainCode); - - /* getBlanking might clip exposure time to the fps limits. */ - Duration exposure = agcStatus->shutterTime; - auto [vblank, hblank] = helper_->getBlanking(exposure, minFrameDuration_, maxFrameDuration_); - int32_t exposureLines = helper_->exposureLines(exposure, - helper_->hblankToLineLength(hblank)); - - LOG(IPARPI, Debug) << "Applying AGC Exposure: " << exposure - << " (Shutter lines: " << exposureLines << ", AGC requested " - << agcStatus->shutterTime << ") Gain: " - << agcStatus->analogueGain << " (Gain Code: " - << gainCode << ")"; - - ctrls.set(V4L2_CID_VBLANK, static_cast(vblank)); - ctrls.set(V4L2_CID_EXPOSURE, exposureLines); - ctrls.set(V4L2_CID_ANALOGUE_GAIN, gainCode); - - /* - * At present, there is no way of knowing if a control is read-only. - * As a workaround, assume that if the minimum and maximum values of - * the V4L2_CID_HBLANK control are the same, it implies the control - * is read-only. This seems to be the case for all the cameras our IPA - * works with. - * - * \todo The control API ought to have a flag to specify if a control - * is read-only which could be used below. - */ - if (mode_.minLineLength != mode_.maxLineLength) - ctrls.set(V4L2_CID_HBLANK, static_cast(hblank)); - - /* - * Store the frame length times in a circular queue, up-to FrameLengthsQueueSize - * elements. This will be used to advertise a camera timeout value to the - * pipeline handler. - */ - frameLengths_.pop_front(); - frameLengths_.push_back(helper_->exposure(vblank + mode_.height, - helper_->hblankToLineLength(hblank))); -} - -void IPARPi::applyDG(const struct AgcStatus *dgStatus, ControlList &ctrls) -{ - ctrls.set(V4L2_CID_DIGITAL_GAIN, - static_cast(dgStatus->digitalGain * 1000)); -} - -void IPARPi::applyCCM(const struct CcmStatus *ccmStatus, ControlList &ctrls) -{ - bcm2835_isp_custom_ccm ccm; - - for (int i = 0; i < 9; i++) { - ccm.ccm.ccm[i / 3][i % 3].den = 1000; - ccm.ccm.ccm[i / 3][i % 3].num = 1000 * ccmStatus->matrix[i]; - } - - ccm.enabled = 1; - ccm.ccm.offsets[0] = ccm.ccm.offsets[1] = ccm.ccm.offsets[2] = 0; - - ControlValue c(Span{ reinterpret_cast(&ccm), - sizeof(ccm) }); - ctrls.set(V4L2_CID_USER_BCM2835_ISP_CC_MATRIX, c); -} - -void IPARPi::applyGamma(const struct ContrastStatus *contrastStatus, ControlList &ctrls) -{ - const unsigned int numGammaPoints = controller_.getHardwareConfig().numGammaPoints; - struct bcm2835_isp_gamma gamma; - - for (unsigned int i = 0; i < numGammaPoints - 1; i++) { - int x = i < 16 ? i * 1024 - : (i < 24 ? (i - 16) * 2048 + 16384 - : (i - 24) * 4096 + 32768); - gamma.x[i] = x; - gamma.y[i] = std::min(65535, contrastStatus->gammaCurve.eval(x)); - } - - gamma.x[numGammaPoints - 1] = 65535; - gamma.y[numGammaPoints - 1] = 65535; - gamma.enabled = 1; - - ControlValue c(Span{ reinterpret_cast(&gamma), - sizeof(gamma) }); - ctrls.set(V4L2_CID_USER_BCM2835_ISP_GAMMA, c); -} - -void IPARPi::applyBlackLevel(const struct BlackLevelStatus *blackLevelStatus, ControlList &ctrls) -{ - bcm2835_isp_black_level blackLevel; - - blackLevel.enabled = 1; - blackLevel.black_level_r = blackLevelStatus->blackLevelR; - blackLevel.black_level_g = blackLevelStatus->blackLevelG; - blackLevel.black_level_b = blackLevelStatus->blackLevelB; - - ControlValue c(Span{ reinterpret_cast(&blackLevel), - sizeof(blackLevel) }); - ctrls.set(V4L2_CID_USER_BCM2835_ISP_BLACK_LEVEL, c); -} - -void IPARPi::applyGEQ(const struct GeqStatus *geqStatus, ControlList &ctrls) -{ - bcm2835_isp_geq geq; - - geq.enabled = 1; - geq.offset = geqStatus->offset; - geq.slope.den = 1000; - geq.slope.num = 1000 * geqStatus->slope; - - ControlValue c(Span{ reinterpret_cast(&geq), - sizeof(geq) }); - ctrls.set(V4L2_CID_USER_BCM2835_ISP_GEQ, c); -} - -void IPARPi::applyDenoise(const struct DenoiseStatus *denoiseStatus, ControlList &ctrls) -{ - using RPiController::DenoiseMode; - - bcm2835_isp_denoise denoise; - DenoiseMode mode = static_cast(denoiseStatus->mode); - - denoise.enabled = mode != DenoiseMode::Off; - denoise.constant = denoiseStatus->noiseConstant; - denoise.slope.num = 1000 * denoiseStatus->noiseSlope; - denoise.slope.den = 1000; - denoise.strength.num = 1000 * denoiseStatus->strength; - denoise.strength.den = 1000; - - /* Set the CDN mode to match the SDN operating mode. */ - bcm2835_isp_cdn cdn; - switch (mode) { - case DenoiseMode::ColourFast: - cdn.enabled = 1; - cdn.mode = CDN_MODE_FAST; - break; - case DenoiseMode::ColourHighQuality: - cdn.enabled = 1; - cdn.mode = CDN_MODE_HIGH_QUALITY; - break; - default: - cdn.enabled = 0; - } - - ControlValue c(Span{ reinterpret_cast(&denoise), - sizeof(denoise) }); - ctrls.set(V4L2_CID_USER_BCM2835_ISP_DENOISE, c); - - c = ControlValue(Span{ reinterpret_cast(&cdn), - sizeof(cdn) }); - ctrls.set(V4L2_CID_USER_BCM2835_ISP_CDN, c); -} - -void IPARPi::applySharpen(const struct SharpenStatus *sharpenStatus, ControlList &ctrls) -{ - bcm2835_isp_sharpen sharpen; - - sharpen.enabled = 1; - sharpen.threshold.num = 1000 * sharpenStatus->threshold; - sharpen.threshold.den = 1000; - sharpen.strength.num = 1000 * sharpenStatus->strength; - sharpen.strength.den = 1000; - sharpen.limit.num = 1000 * sharpenStatus->limit; - sharpen.limit.den = 1000; - - ControlValue c(Span{ reinterpret_cast(&sharpen), - sizeof(sharpen) }); - ctrls.set(V4L2_CID_USER_BCM2835_ISP_SHARPEN, c); -} - -void IPARPi::applyDPC(const struct DpcStatus *dpcStatus, ControlList &ctrls) -{ - bcm2835_isp_dpc dpc; - - dpc.enabled = 1; - dpc.strength = dpcStatus->strength; - - ControlValue c(Span{ reinterpret_cast(&dpc), - sizeof(dpc) }); - ctrls.set(V4L2_CID_USER_BCM2835_ISP_DPC, c); -} - -void IPARPi::applyLS(const struct AlscStatus *lsStatus, ControlList &ctrls) -{ - /* - * Program lens shading tables into pipeline. - * Choose smallest cell size that won't exceed 63x48 cells. - */ - const int cellSizes[] = { 16, 32, 64, 128, 256 }; - unsigned int numCells = std::size(cellSizes); - unsigned int i, w, h, cellSize; - for (i = 0; i < numCells; i++) { - cellSize = cellSizes[i]; - w = (mode_.width + cellSize - 1) / cellSize; - h = (mode_.height + cellSize - 1) / cellSize; - if (w < 64 && h <= 48) - break; - } - - if (i == numCells) { - LOG(IPARPI, Error) << "Cannot find cell size"; - return; - } - - /* We're going to supply corner sampled tables, 16 bit samples. */ - w++, h++; - bcm2835_isp_lens_shading ls = { - .enabled = 1, - .grid_cell_size = cellSize, - .grid_width = w, - .grid_stride = w, - .grid_height = h, - /* .dmabuf will be filled in by pipeline handler. */ - .dmabuf = 0, - .ref_transform = 0, - .corner_sampled = 1, - .gain_format = GAIN_FORMAT_U4P10 - }; - - if (!lsTable_ || w * h * 4 * sizeof(uint16_t) > MaxLsGridSize) { - LOG(IPARPI, Error) << "Do not have a correctly allocate lens shading table!"; - return; - } - - if (lsStatus) { - /* Format will be u4.10 */ - uint16_t *grid = static_cast(lsTable_); - - resampleTable(grid, lsStatus->r, w, h); - resampleTable(grid + w * h, lsStatus->g, w, h); - std::memcpy(grid + 2 * w * h, grid + w * h, w * h * sizeof(uint16_t)); - resampleTable(grid + 3 * w * h, lsStatus->b, w, h); - } - - ControlValue c(Span{ reinterpret_cast(&ls), - sizeof(ls) }); - ctrls.set(V4L2_CID_USER_BCM2835_ISP_LENS_SHADING, c); -} - -void IPARPi::applyAF(const struct AfStatus *afStatus, ControlList &lensCtrls) -{ - if (afStatus->lensSetting) { - ControlValue v(afStatus->lensSetting.value()); - lensCtrls.set(V4L2_CID_FOCUS_ABSOLUTE, v); - } -} - -/* - * Resamples a 16x12 table with central sampling to destW x destH with corner - * sampling. - */ -void IPARPi::resampleTable(uint16_t dest[], const std::vector &src, - int destW, int destH) -{ - /* - * Precalculate and cache the x sampling locations and phases to - * save recomputing them on every row. - */ - assert(destW > 1 && destH > 1 && destW <= 64); - int xLo[64], xHi[64]; - double xf[64]; - double x = -0.5, xInc = 16.0 / (destW - 1); - for (int i = 0; i < destW; i++, x += xInc) { - xLo[i] = floor(x); - xf[i] = x - xLo[i]; - xHi[i] = xLo[i] < 15 ? xLo[i] + 1 : 15; - xLo[i] = xLo[i] > 0 ? xLo[i] : 0; - } - - /* Now march over the output table generating the new values. */ - double y = -0.5, yInc = 12.0 / (destH - 1); - for (int j = 0; j < destH; j++, y += yInc) { - int yLo = floor(y); - double yf = y - yLo; - int yHi = yLo < 11 ? yLo + 1 : 11; - yLo = yLo > 0 ? yLo : 0; - double const *rowAbove = src.data() + yLo * 16; - double const *rowBelow = src.data() + yHi * 16; - for (int i = 0; i < destW; i++) { - double above = rowAbove[xLo[i]] * (1 - xf[i]) + rowAbove[xHi[i]] * xf[i]; - double below = rowBelow[xLo[i]] * (1 - xf[i]) + rowBelow[xHi[i]] * xf[i]; - int result = floor(1024 * (above * (1 - yf) + below * yf) + .5); - *(dest++) = result > 16383 ? 16383 : result; /* want u4.10 */ - } - } -} - -} /* namespace ipa::RPi */ - -/* - * External IPA module interface - */ -extern "C" { -const struct IPAModuleInfo ipaModuleInfo = { - IPA_MODULE_API_VERSION, - 1, - "PipelineHandlerRPi", - "raspberrypi", -}; - -IPAInterface *ipaCreate() -{ - return new ipa::RPi::IPARPi(); -} - -} /* extern "C" */ - -} /* namespace libcamera */ diff --git a/src/ipa/raspberrypi/statistics.h b/src/ipa/raspberrypi/statistics.h deleted file mode 100644 index 015d4efc..00000000 --- a/src/ipa/raspberrypi/statistics.h +++ /dev/null @@ -1,78 +0,0 @@ -/* SPDX-License-Identifier: BSD-2-Clause */ -/* - * Copyright (C) 2022, Raspberry Pi Ltd - * - * statistics.h - Raspberry Pi generic statistics structure - */ -#pragma once - -#include -#include -#include - -#include "histogram.h" -#include "region_stats.h" - -namespace RPiController { - -struct RgbySums { - RgbySums(uint64_t _rSum = 0, uint64_t _gSum = 0, uint64_t _bSum = 0, uint64_t _ySum = 0) - : rSum(_rSum), gSum(_gSum), bSum(_bSum), ySum(_ySum) - { - } - uint64_t rSum; - uint64_t gSum; - uint64_t bSum; - uint64_t ySum; -}; - -using RgbyRegions = RegionStats; -using FocusRegions = RegionStats; - -struct Statistics { - /* - * All region based statistics are normalised to 16-bits, giving a - * maximum value of (1 << NormalisationFactorPow2) - 1. - */ - static constexpr unsigned int NormalisationFactorPow2 = 16; - - /* - * Positioning of the AGC statistics gathering in the pipeline: - * Pre-WB correction or post-WB correction. - * Assume this is post-LSC. - */ - enum class AgcStatsPos { PreWb, PostWb }; - const AgcStatsPos agcStatsPos; - - /* - * Positioning of the AWB/ALSC statistics gathering in the pipeline: - * Pre-LSC or post-LSC. - */ - enum class ColourStatsPos { PreLsc, PostLsc }; - const ColourStatsPos colourStatsPos; - - Statistics(AgcStatsPos a, ColourStatsPos c) - : agcStatsPos(a), colourStatsPos(c) - { - } - - /* Histogram statistics. Not all histograms may be populated! */ - Histogram rHist; - Histogram gHist; - Histogram bHist; - Histogram yHist; - - /* Row sums for flicker avoidance. */ - std::vector rowSums; - - /* Region based colour sums. */ - RgbyRegions agcRegions; - RgbyRegions awbRegions; - - /* Region based focus FoM. */ - FocusRegions focusRegions; -}; - -using StatisticsPtr = std::shared_ptr; - -} /* namespace RPiController */ diff --git a/src/ipa/rpi/README.md b/src/ipa/rpi/README.md new file mode 100644 index 00000000..94a8ccc8 --- /dev/null +++ b/src/ipa/rpi/README.md @@ -0,0 +1,25 @@ +.. SPDX-License-Identifier: BSD-2-Clause + +# _libcamera_ for the Raspberry Pi + +Raspberry Pi provides a fully featured pipeline handler and control algorithms +(IPAs, or "Image Processing Algorithms") to work with _libcamera_. Support is +included for all existing Raspberry Pi camera modules. + +_libcamera_ for the Raspberry Pi allows users to: + +1. Use their existing Raspberry Pi cameras. +1. Change the tuning of the image processing for their Raspberry Pi cameras. +1. Alter or amend the control algorithms (such as AGC/AEC, AWB or any others) + that control the sensor and ISP. +1. Implement their own custom control algorithms. +1. Supply new tunings and/or algorithms for completely new sensors. + +## How to install and run _libcamera_ on the Raspberry Pi + +Please follow the instructions [here](https://www.raspberrypi.com/documentation/accessories/camera.html). + +## Documentation + +Full documentation for the _Raspberry Pi Camera Algorithm and Tuning Guide_ can +be found [here](https://datasheets.raspberrypi.com/camera/raspberry-pi-camera-guide.pdf). diff --git a/src/ipa/rpi/cam_helper/cam_helper.cpp b/src/ipa/rpi/cam_helper/cam_helper.cpp new file mode 100644 index 00000000..ddd5e9a4 --- /dev/null +++ b/src/ipa/rpi/cam_helper/cam_helper.cpp @@ -0,0 +1,265 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (C) 2019, Raspberry Pi Ltd + * + * cam_helper.cpp - helper information for different sensors + */ + +#include + +#include +#include +#include + +#include "libcamera/internal/v4l2_videodevice.h" + +#include "cam_helper.h" +#include "md_parser.h" + +using namespace RPiController; +using namespace libcamera; +using libcamera::utils::Duration; +using namespace std::literals::chrono_literals; + +namespace libcamera { +LOG_DECLARE_CATEGORY(IPARPI) +} + +namespace { + +std::map &camHelpers() +{ + static std::map helpers; + return helpers; +} + +} /* namespace */ + +CamHelper *CamHelper::create(std::string const &camName) +{ + /* + * CamHelpers get registered by static RegisterCamHelper + * initialisers. + */ + for (auto &p : camHelpers()) { + if (camName.find(p.first) != std::string::npos) + return p.second(); + } + + return nullptr; +} + +CamHelper::CamHelper(std::unique_ptr parser, unsigned int frameIntegrationDiff) + : parser_(std::move(parser)), frameIntegrationDiff_(frameIntegrationDiff) +{ +} + +CamHelper::~CamHelper() +{ +} + +void CamHelper::prepare(Span buffer, + Metadata &metadata) +{ + parseEmbeddedData(buffer, metadata); +} + +void CamHelper::process([[maybe_unused]] StatisticsPtr &stats, + [[maybe_unused]] Metadata &metadata) +{ +} + +uint32_t CamHelper::exposureLines(const Duration exposure, const Duration lineLength) const +{ + return exposure / lineLength; +} + +Duration CamHelper::exposure(uint32_t exposureLines, const Duration lineLength) const +{ + return exposureLines * lineLength; +} + +std::pair CamHelper::getBlanking(Duration &exposure, + Duration minFrameDuration, + Duration maxFrameDuration) const +{ + uint32_t frameLengthMin, frameLengthMax, vblank, hblank; + Duration lineLength = mode_.minLineLength; + + /* + * minFrameDuration and maxFrameDuration are clamped by the caller + * based on the limits for the active sensor mode. + * + * frameLengthMax gets calculated on the smallest line length as we do + * not want to extend that unless absolutely necessary. + */ + frameLengthMin = minFrameDuration / mode_.minLineLength; + frameLengthMax = maxFrameDuration / mode_.minLineLength; + + /* + * Watch out for (exposureLines + frameIntegrationDiff_) overflowing a + * uint32_t in the std::clamp() below when the exposure time is + * extremely (extremely!) long - as happens when the IPA calculates the + * maximum possible exposure time. + */ + uint32_t exposureLines = std::min(CamHelper::exposureLines(exposure, lineLength), + std::numeric_limits::max() - frameIntegrationDiff_); + uint32_t frameLengthLines = std::clamp(exposureLines + frameIntegrationDiff_, + frameLengthMin, frameLengthMax); + + /* + * If our frame length lines is above the maximum allowed, see if we can + * extend the line length to accommodate the requested frame length. + */ + if (frameLengthLines > mode_.maxFrameLength) { + Duration lineLengthAdjusted = lineLength * frameLengthLines / mode_.maxFrameLength; + lineLength = std::min(mode_.maxLineLength, lineLengthAdjusted); + frameLengthLines = mode_.maxFrameLength; + } + + hblank = lineLengthToHblank(lineLength); + vblank = frameLengthLines - mode_.height; + + /* + * Limit the exposure to the maximum frame duration requested, and + * re-calculate if it has been clipped. + */ + exposureLines = std::min(frameLengthLines - frameIntegrationDiff_, + CamHelper::exposureLines(exposure, lineLength)); + exposure = CamHelper::exposure(exposureLines, lineLength); + + return { vblank, hblank }; +} + +Duration CamHelper::hblankToLineLength(uint32_t hblank) const +{ + return (mode_.width + hblank) * (1.0s / mode_.pixelRate); +} + +uint32_t CamHelper::lineLengthToHblank(const Duration &lineLength) const +{ + return (lineLength * mode_.pixelRate / 1.0s) - mode_.width; +} + +Duration CamHelper::lineLengthPckToDuration(uint32_t lineLengthPck) const +{ + return lineLengthPck * (1.0s / mode_.pixelRate); +} + +void CamHelper::setCameraMode(const CameraMode &mode) +{ + mode_ = mode; + if (parser_) { + parser_->reset(); + parser_->setBitsPerPixel(mode.bitdepth); + parser_->setLineLengthBytes(0); /* We use SetBufferSize. */ + } +} + +void CamHelper::getDelays(int &exposureDelay, int &gainDelay, + int &vblankDelay, int &hblankDelay) const +{ + /* + * These values are correct for many sensors. Other sensors will + * need to over-ride this function. + */ + exposureDelay = 2; + gainDelay = 1; + vblankDelay = 2; + hblankDelay = 2; +} + +bool CamHelper::sensorEmbeddedDataPresent() const +{ + return false; +} + +double CamHelper::getModeSensitivity([[maybe_unused]] const CameraMode &mode) const +{ + /* + * Most sensors have the same sensitivity in every mode, but this + * function can be overridden for those that do not. Note that it is + * called before mode_ is set, so it must return the sensitivity + * of the mode that is passed in. + */ + return 1.0; +} + +unsigned int CamHelper::hideFramesStartup() const +{ + /* + * The number of frames when a camera first starts that shouldn't be + * displayed as they are invalid in some way. + */ + return 0; +} + +unsigned int CamHelper::hideFramesModeSwitch() const +{ + /* After a mode switch, many sensors return valid frames immediately. */ + return 0; +} + +unsigned int CamHelper::mistrustFramesStartup() const +{ + /* Many sensors return a single bad frame on start-up. */ + return 1; +} + +unsigned int CamHelper::mistrustFramesModeSwitch() const +{ + /* Many sensors return valid metadata immediately. */ + return 0; +} + +void CamHelper::parseEmbeddedData(Span buffer, + Metadata &metadata) +{ + MdParser::RegisterMap registers; + Metadata parsedMetadata; + + if (buffer.empty()) + return; + + if (parser_->parse(buffer, registers) != MdParser::Status::OK) { + LOG(IPARPI, Error) << "Embedded data buffer parsing failed"; + return; + } + + populateMetadata(registers, parsedMetadata); + metadata.merge(parsedMetadata); + + /* + * Overwrite the exposure/gain, line/frame length and sensor temperature values + * in the existing DeviceStatus with values from the parsed embedded buffer. + * Fetch it first in case any other fields were set meaningfully. + */ + DeviceStatus deviceStatus, parsedDeviceStatus; + if (metadata.get("device.status", deviceStatus) || + parsedMetadata.get("device.status", parsedDeviceStatus)) { + LOG(IPARPI, Error) << "DeviceStatus not found"; + return; + } + + deviceStatus.shutterSpeed = parsedDeviceStatus.shutterSpeed; + deviceStatus.analogueGain = parsedDeviceStatus.analogueGain; + deviceStatus.frameLength = parsedDeviceStatus.frameLength; + deviceStatus.lineLength = parsedDeviceStatus.lineLength; + if (parsedDeviceStatus.sensorTemperature) + deviceStatus.sensorTemperature = parsedDeviceStatus.sensorTemperature; + + LOG(IPARPI, Debug) << "Metadata updated - " << deviceStatus; + + metadata.set("device.status", deviceStatus); +} + +void CamHelper::populateMetadata([[maybe_unused]] const MdParser::RegisterMap ®isters, + [[maybe_unused]] Metadata &metadata) const +{ +} + +RegisterCamHelper::RegisterCamHelper(char const *camName, + CamHelperCreateFunc createFunc) +{ + camHelpers()[std::string(camName)] = createFunc; +} diff --git a/src/ipa/rpi/cam_helper/cam_helper.h b/src/ipa/rpi/cam_helper/cam_helper.h new file mode 100644 index 00000000..58a4b202 --- /dev/null +++ b/src/ipa/rpi/cam_helper/cam_helper.h @@ -0,0 +1,132 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (C) 2019, Raspberry Pi Ltd + * + * cam_helper.h - helper class providing camera information + */ +#pragma once + +#include +#include +#include + +#include +#include + +#include "controller/camera_mode.h" +#include "controller/controller.h" +#include "controller/metadata.h" +#include "md_parser.h" + +#include "libcamera/internal/v4l2_videodevice.h" + +namespace RPiController { + +/* + * The CamHelper class provides a number of facilities that anyone trying + * to drive a camera will need to know, but which are not provided by the + * standard driver framework. Specifically, it provides: + * + * A "CameraMode" structure to describe extra information about the chosen + * mode of the driver. For example, how it is cropped from the full sensor + * area, how it is scaled, whether pixels are averaged compared to the full + * resolution. + * + * The ability to convert between number of lines of exposure and actual + * exposure time, and to convert between the sensor's gain codes and actual + * gains. + * + * A function to return the number of frames of delay between updating exposure, + * analogue gain and vblanking, and for the changes to take effect. For many + * sensors these take the values 2, 1 and 2 respectively, but sensors that are + * different will need to over-ride the default function provided. + * + * A function to query if the sensor outputs embedded data that can be parsed. + * + * A function to return the sensitivity of a given camera mode. + * + * A parser to parse the embedded data buffers provided by some sensors (for + * example, the imx219 does; the ov5647 doesn't). This allows us to know for + * sure the exposure and gain of the frame we're looking at. CamHelper + * provides functions for converting analogue gains to and from the sensor's + * native gain codes. + * + * Finally, a set of functions that determine how to handle the vagaries of + * different camera modules on start-up or when switching modes. Some + * modules may produce one or more frames that are not yet correctly exposed, + * or where the metadata may be suspect. We have the following functions: + * HideFramesStartup(): Tell the pipeline handler not to return this many + * frames at start-up. This can also be used to hide initial frames + * while the AGC and other algorithms are sorting themselves out. + * HideFramesModeSwitch(): Tell the pipeline handler not to return this + * many frames after a mode switch (other than start-up). Some sensors + * may produce innvalid frames after a mode switch; others may not. + * MistrustFramesStartup(): At start-up a sensor may return frames for + * which we should not run any control algorithms (for example, metadata + * may be invalid). + * MistrustFramesModeSwitch(): The number of frames, after a mode switch + * (other than start-up), for which control algorithms should not run + * (for example, metadata may be unreliable). + */ + +class CamHelper +{ +public: + static CamHelper *create(std::string const &camName); + CamHelper(std::unique_ptr parser, unsigned int frameIntegrationDiff); + virtual ~CamHelper(); + void setCameraMode(const CameraMode &mode); + virtual void prepare(libcamera::Span buffer, + Metadata &metadata); + virtual void process(StatisticsPtr &stats, Metadata &metadata); + virtual uint32_t exposureLines(const libcamera::utils::Duration exposure, + const libcamera::utils::Duration lineLength) const; + virtual libcamera::utils::Duration exposure(uint32_t exposureLines, + const libcamera::utils::Duration lineLength) const; + virtual std::pair getBlanking(libcamera::utils::Duration &exposure, + libcamera::utils::Duration minFrameDuration, + libcamera::utils::Duration maxFrameDuration) const; + libcamera::utils::Duration hblankToLineLength(uint32_t hblank) const; + uint32_t lineLengthToHblank(const libcamera::utils::Duration &duration) const; + libcamera::utils::Duration lineLengthPckToDuration(uint32_t lineLengthPck) const; + virtual uint32_t gainCode(double gain) const = 0; + virtual double gain(uint32_t gainCode) const = 0; + virtual void getDelays(int &exposureDelay, int &gainDelay, + int &vblankDelay, int &hblankDelay) const; + virtual bool sensorEmbeddedDataPresent() const; + virtual double getModeSensitivity(const CameraMode &mode) const; + virtual unsigned int hideFramesStartup() const; + virtual unsigned int hideFramesModeSwitch() const; + virtual unsigned int mistrustFramesStartup() const; + virtual unsigned int mistrustFramesModeSwitch() const; + +protected: + void parseEmbeddedData(libcamera::Span buffer, + Metadata &metadata); + virtual void populateMetadata(const MdParser::RegisterMap ®isters, + Metadata &metadata) const; + + std::unique_ptr parser_; + CameraMode mode_; + +private: + /* + * Smallest difference between the frame length and integration time, + * in units of lines. + */ + unsigned int frameIntegrationDiff_; +}; + +/* + * This is for registering camera helpers with the system, so that the + * CamHelper::Create function picks them up automatically. + */ + +typedef CamHelper *(*CamHelperCreateFunc)(); +struct RegisterCamHelper +{ + RegisterCamHelper(char const *camName, + CamHelperCreateFunc createFunc); +}; + +} /* namespace RPi */ diff --git a/src/ipa/rpi/cam_helper/cam_helper_imx219.cpp b/src/ipa/rpi/cam_helper/cam_helper_imx219.cpp new file mode 100644 index 00000000..c3337ed0 --- /dev/null +++ b/src/ipa/rpi/cam_helper/cam_helper_imx219.cpp @@ -0,0 +1,115 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (C) 2019, Raspberry Pi Ltd + * + * cam_helper_imx219.cpp - camera helper for imx219 sensor + */ + +#include +#include +#include +#include + +/* + * We have observed that the imx219 embedded data stream randomly returns junk + * register values. Do not rely on embedded data until this has been resolved. + */ +#define ENABLE_EMBEDDED_DATA 0 + +#include "cam_helper.h" +#if ENABLE_EMBEDDED_DATA +#include "md_parser.h" +#endif + +using namespace RPiController; + +/* + * We care about one gain register and a pair of exposure registers. Their I2C + * addresses from the Sony IMX219 datasheet: + */ +constexpr uint32_t gainReg = 0x157; +constexpr uint32_t expHiReg = 0x15a; +constexpr uint32_t expLoReg = 0x15b; +constexpr uint32_t frameLengthHiReg = 0x160; +constexpr uint32_t frameLengthLoReg = 0x161; +constexpr uint32_t lineLengthHiReg = 0x162; +constexpr uint32_t lineLengthLoReg = 0x163; +constexpr std::initializer_list registerList [[maybe_unused]] + = { expHiReg, expLoReg, gainReg, frameLengthHiReg, frameLengthLoReg, + lineLengthHiReg, lineLengthLoReg }; + +class CamHelperImx219 : public CamHelper +{ +public: + CamHelperImx219(); + uint32_t gainCode(double gain) const override; + double gain(uint32_t gainCode) const override; + unsigned int mistrustFramesModeSwitch() const override; + bool sensorEmbeddedDataPresent() const override; + +private: + /* + * Smallest difference between the frame length and integration time, + * in units of lines. + */ + static constexpr int frameIntegrationDiff = 4; + + void populateMetadata(const MdParser::RegisterMap ®isters, + Metadata &metadata) const override; +}; + +CamHelperImx219::CamHelperImx219() +#if ENABLE_EMBEDDED_DATA + : CamHelper(std::make_unique(registerList), frameIntegrationDiff) +#else + : CamHelper({}, frameIntegrationDiff) +#endif +{ +} + +uint32_t CamHelperImx219::gainCode(double gain) const +{ + return (uint32_t)(256 - 256 / gain); +} + +double CamHelperImx219::gain(uint32_t gainCode) const +{ + return 256.0 / (256 - gainCode); +} + +unsigned int CamHelperImx219::mistrustFramesModeSwitch() const +{ + /* + * For reasons unknown, we do occasionally get a bogus metadata frame + * at a mode switch (though not at start-up). Possibly warrants some + * investigation, though not a big deal. + */ + return 1; +} + +bool CamHelperImx219::sensorEmbeddedDataPresent() const +{ + return ENABLE_EMBEDDED_DATA; +} + +void CamHelperImx219::populateMetadata(const MdParser::RegisterMap ®isters, + Metadata &metadata) const +{ + DeviceStatus deviceStatus; + + deviceStatus.lineLength = lineLengthPckToDuration(registers.at(lineLengthHiReg) * 256 + + registers.at(lineLengthLoReg)); + deviceStatus.shutterSpeed = exposure(registers.at(expHiReg) * 256 + registers.at(expLoReg), + deviceStatus.lineLength); + deviceStatus.analogueGain = gain(registers.at(gainReg)); + deviceStatus.frameLength = registers.at(frameLengthHiReg) * 256 + registers.at(frameLengthLoReg); + + metadata.set("device.status", deviceStatus); +} + +static CamHelper *create() +{ + return new CamHelperImx219(); +} + +static RegisterCamHelper reg("imx219", &create); diff --git a/src/ipa/rpi/cam_helper/cam_helper_imx290.cpp b/src/ipa/rpi/cam_helper/cam_helper_imx290.cpp new file mode 100644 index 00000000..7d6f5b54 --- /dev/null +++ b/src/ipa/rpi/cam_helper/cam_helper_imx290.cpp @@ -0,0 +1,68 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (C) 2021, Raspberry Pi Ltd + * + * cam_helper_imx290.cpp - camera helper for imx290 sensor + */ + +#include + +#include "cam_helper.h" + +using namespace RPiController; + +class CamHelperImx290 : public CamHelper +{ +public: + CamHelperImx290(); + uint32_t gainCode(double gain) const override; + double gain(uint32_t gainCode) const override; + void getDelays(int &exposureDelay, int &gainDelay, + int &vblankDelay, int &hblankDelay) const override; + unsigned int hideFramesModeSwitch() const override; + +private: + /* + * Smallest difference between the frame length and integration time, + * in units of lines. + */ + static constexpr int frameIntegrationDiff = 2; +}; + +CamHelperImx290::CamHelperImx290() + : CamHelper({}, frameIntegrationDiff) +{ +} + +uint32_t CamHelperImx290::gainCode(double gain) const +{ + int code = 66.6667 * log10(gain); + return std::max(0, std::min(code, 0xf0)); +} + +double CamHelperImx290::gain(uint32_t gainCode) const +{ + return pow(10, 0.015 * gainCode); +} + +void CamHelperImx290::getDelays(int &exposureDelay, int &gainDelay, + int &vblankDelay, int &hblankDelay) const +{ + exposureDelay = 2; + gainDelay = 2; + vblankDelay = 2; + hblankDelay = 2; +} + +unsigned int CamHelperImx290::hideFramesModeSwitch() const +{ + /* After a mode switch, we seem to get 1 bad frame. */ + return 1; +} + +static CamHelper *create() +{ + return new CamHelperImx290(); +} + +static RegisterCamHelper reg("imx290", &create); diff --git a/src/ipa/rpi/cam_helper/cam_helper_imx296.cpp b/src/ipa/rpi/cam_helper/cam_helper_imx296.cpp new file mode 100644 index 00000000..ecb845e7 --- /dev/null +++ b/src/ipa/rpi/cam_helper/cam_helper_imx296.cpp @@ -0,0 +1,83 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (C) 2020, Raspberry Pi Ltd + * + * cam_helper_imx296.cpp - Camera helper for IMX296 sensor + */ + +#include +#include +#include + +#include "cam_helper.h" + +using namespace RPiController; +using libcamera::utils::Duration; +using namespace std::literals::chrono_literals; + +class CamHelperImx296 : public CamHelper +{ +public: + CamHelperImx296(); + uint32_t gainCode(double gain) const override; + double gain(uint32_t gainCode) const override; + uint32_t exposureLines(const Duration exposure, const Duration lineLength) const override; + Duration exposure(uint32_t exposureLines, const Duration lineLength) const override; + void getDelays(int &exposureDelay, int &gainDelay, + int &vblankDelay, int &hblankDelay) const override; + +private: + static constexpr uint32_t minExposureLines = 1; + static constexpr uint32_t maxGainCode = 239; + static constexpr Duration timePerLine = 550.0 / 37.125e6 * 1.0s; + + /* + * Smallest difference between the frame length and integration time, + * in units of lines. + */ + static constexpr int frameIntegrationDiff = 4; +}; + +CamHelperImx296::CamHelperImx296() + : CamHelper(nullptr, frameIntegrationDiff) +{ +} + +uint32_t CamHelperImx296::gainCode(double gain) const +{ + uint32_t code = 20 * std::log10(gain) * 10; + return std::min(code, maxGainCode); +} + +double CamHelperImx296::gain(uint32_t gainCode) const +{ + return std::pow(10.0, gainCode / 200.0); +} + +uint32_t CamHelperImx296::exposureLines(const Duration exposure, + [[maybe_unused]] const Duration lineLength) const +{ + return std::max(minExposureLines, (exposure - 14.26us) / timePerLine); +} + +Duration CamHelperImx296::exposure(uint32_t exposureLines, + [[maybe_unused]] const Duration lineLength) const +{ + return std::max(minExposureLines, exposureLines) * timePerLine + 14.26us; +} + +void CamHelperImx296::getDelays(int &exposureDelay, int &gainDelay, + int &vblankDelay, int &hblankDelay) const +{ + exposureDelay = 2; + gainDelay = 2; + vblankDelay = 2; + hblankDelay = 2; +} + +static CamHelper *create() +{ + return new CamHelperImx296(); +} + +static RegisterCamHelper reg("imx296", &create); diff --git a/src/ipa/rpi/cam_helper/cam_helper_imx477.cpp b/src/ipa/rpi/cam_helper/cam_helper_imx477.cpp new file mode 100644 index 00000000..bc769ca7 --- /dev/null +++ b/src/ipa/rpi/cam_helper/cam_helper_imx477.cpp @@ -0,0 +1,197 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (C) 2020, Raspberry Pi Ltd + * + * cam_helper_imx477.cpp - camera helper for imx477 sensor + */ + +#include +#include +#include +#include +#include +#include + +#include + +#include "cam_helper.h" +#include "md_parser.h" + +using namespace RPiController; +using namespace libcamera; +using libcamera::utils::Duration; + +namespace libcamera { +LOG_DECLARE_CATEGORY(IPARPI) +} + +/* + * We care about two gain registers and a pair of exposure registers. Their + * I2C addresses from the Sony IMX477 datasheet: + */ +constexpr uint32_t expHiReg = 0x0202; +constexpr uint32_t expLoReg = 0x0203; +constexpr uint32_t gainHiReg = 0x0204; +constexpr uint32_t gainLoReg = 0x0205; +constexpr uint32_t frameLengthHiReg = 0x0340; +constexpr uint32_t frameLengthLoReg = 0x0341; +constexpr uint32_t lineLengthHiReg = 0x0342; +constexpr uint32_t lineLengthLoReg = 0x0343; +constexpr uint32_t temperatureReg = 0x013a; +constexpr std::initializer_list registerList = + { expHiReg, expLoReg, gainHiReg, gainLoReg, frameLengthHiReg, frameLengthLoReg, + lineLengthHiReg, lineLengthLoReg, temperatureReg }; + +class CamHelperImx477 : public CamHelper +{ +public: + CamHelperImx477(); + uint32_t gainCode(double gain) const override; + double gain(uint32_t gainCode) const override; + void prepare(libcamera::Span buffer, Metadata &metadata) override; + std::pair getBlanking(Duration &exposure, Duration minFrameDuration, + Duration maxFrameDuration) const override; + void getDelays(int &exposureDelay, int &gainDelay, + int &vblankDelay, int &hblankDelay) const override; + bool sensorEmbeddedDataPresent() const override; + +private: + /* + * Smallest difference between the frame length and integration time, + * in units of lines. + */ + static constexpr int frameIntegrationDiff = 22; + /* Maximum frame length allowable for long exposure calculations. */ + static constexpr int frameLengthMax = 0xffdc; + /* Largest long exposure scale factor given as a left shift on the frame length. */ + static constexpr int longExposureShiftMax = 7; + + void populateMetadata(const MdParser::RegisterMap ®isters, + Metadata &metadata) const override; +}; + +CamHelperImx477::CamHelperImx477() + : CamHelper(std::make_unique(registerList), frameIntegrationDiff) +{ +} + +uint32_t CamHelperImx477::gainCode(double gain) const +{ + return static_cast(1024 - 1024 / gain); +} + +double CamHelperImx477::gain(uint32_t gainCode) const +{ + return 1024.0 / (1024 - gainCode); +} + +void CamHelperImx477::prepare(libcamera::Span buffer, Metadata &metadata) +{ + MdParser::RegisterMap registers; + DeviceStatus deviceStatus; + + if (metadata.get("device.status", deviceStatus)) { + LOG(IPARPI, Error) << "DeviceStatus not found from DelayedControls"; + return; + } + + parseEmbeddedData(buffer, metadata); + + /* + * The DeviceStatus struct is first populated with values obtained from + * DelayedControls. If this reports frame length is > frameLengthMax, + * it means we are using a long exposure mode. Since the long exposure + * scale factor is not returned back through embedded data, we must rely + * on the existing exposure lines and frame length values returned by + * DelayedControls. + * + * Otherwise, all values are updated with what is reported in the + * embedded data. + */ + if (deviceStatus.frameLength > frameLengthMax) { + DeviceStatus parsedDeviceStatus; + + metadata.get("device.status", parsedDeviceStatus); + parsedDeviceStatus.shutterSpeed = deviceStatus.shutterSpeed; + parsedDeviceStatus.frameLength = deviceStatus.frameLength; + metadata.set("device.status", parsedDeviceStatus); + + LOG(IPARPI, Debug) << "Metadata updated for long exposure: " + << parsedDeviceStatus; + } +} + +std::pair CamHelperImx477::getBlanking(Duration &exposure, + Duration minFrameDuration, + Duration maxFrameDuration) const +{ + uint32_t frameLength, exposureLines; + unsigned int shift = 0; + + auto [vblank, hblank] = CamHelper::getBlanking(exposure, minFrameDuration, + maxFrameDuration); + + frameLength = mode_.height + vblank; + Duration lineLength = hblankToLineLength(hblank); + + /* + * Check if the frame length calculated needs to be setup for long + * exposure mode. This will require us to use a long exposure scale + * factor provided by a shift operation in the sensor. + */ + while (frameLength > frameLengthMax) { + if (++shift > longExposureShiftMax) { + shift = longExposureShiftMax; + frameLength = frameLengthMax; + break; + } + frameLength >>= 1; + } + + if (shift) { + /* Account for any rounding in the scaled frame length value. */ + frameLength <<= shift; + exposureLines = CamHelperImx477::exposureLines(exposure, lineLength); + exposureLines = std::min(exposureLines, frameLength - frameIntegrationDiff); + exposure = CamHelperImx477::exposure(exposureLines, lineLength); + } + + return { frameLength - mode_.height, hblank }; +} + +void CamHelperImx477::getDelays(int &exposureDelay, int &gainDelay, + int &vblankDelay, int &hblankDelay) const +{ + exposureDelay = 2; + gainDelay = 2; + vblankDelay = 3; + hblankDelay = 3; +} + +bool CamHelperImx477::sensorEmbeddedDataPresent() const +{ + return true; +} + +void CamHelperImx477::populateMetadata(const MdParser::RegisterMap ®isters, + Metadata &metadata) const +{ + DeviceStatus deviceStatus; + + deviceStatus.lineLength = lineLengthPckToDuration(registers.at(lineLengthHiReg) * 256 + + registers.at(lineLengthLoReg)); + deviceStatus.shutterSpeed = exposure(registers.at(expHiReg) * 256 + registers.at(expLoReg), + deviceStatus.lineLength); + deviceStatus.analogueGain = gain(registers.at(gainHiReg) * 256 + registers.at(gainLoReg)); + deviceStatus.frameLength = registers.at(frameLengthHiReg) * 256 + registers.at(frameLengthLoReg); + deviceStatus.sensorTemperature = std::clamp(registers.at(temperatureReg), -20, 80); + + metadata.set("device.status", deviceStatus); +} + +static CamHelper *create() +{ + return new CamHelperImx477(); +} + +static RegisterCamHelper reg("imx477", &create); diff --git a/src/ipa/rpi/cam_helper/cam_helper_imx519.cpp b/src/ipa/rpi/cam_helper/cam_helper_imx519.cpp new file mode 100644 index 00000000..c7262aa0 --- /dev/null +++ b/src/ipa/rpi/cam_helper/cam_helper_imx519.cpp @@ -0,0 +1,196 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Based on cam_helper_imx477.cpp + * Copyright (C) 2020, Raspberry Pi Ltd + * + * cam_helper_imx519.cpp - camera helper for imx519 sensor + * Copyright (C) 2021, Arducam Technology co., Ltd. + */ + +#include +#include +#include +#include +#include + +#include + +#include "cam_helper.h" +#include "md_parser.h" + +using namespace RPiController; +using namespace libcamera; +using libcamera::utils::Duration; + +namespace libcamera { +LOG_DECLARE_CATEGORY(IPARPI) +} + +/* + * We care about two gain registers and a pair of exposure registers. Their + * I2C addresses from the Sony IMX519 datasheet: + */ +constexpr uint32_t expHiReg = 0x0202; +constexpr uint32_t expLoReg = 0x0203; +constexpr uint32_t gainHiReg = 0x0204; +constexpr uint32_t gainLoReg = 0x0205; +constexpr uint32_t frameLengthHiReg = 0x0340; +constexpr uint32_t frameLengthLoReg = 0x0341; +constexpr uint32_t lineLengthHiReg = 0x0342; +constexpr uint32_t lineLengthLoReg = 0x0343; +constexpr std::initializer_list registerList = + { expHiReg, expLoReg, gainHiReg, gainLoReg, frameLengthHiReg, frameLengthLoReg, + lineLengthHiReg, lineLengthLoReg }; + +class CamHelperImx519 : public CamHelper +{ +public: + CamHelperImx519(); + uint32_t gainCode(double gain) const override; + double gain(uint32_t gainCode) const override; + void prepare(libcamera::Span buffer, Metadata &metadata) override; + std::pair getBlanking(Duration &exposure, Duration minFrameDuration, + Duration maxFrameDuration) const override; + void getDelays(int &exposureDelay, int &gainDelay, + int &vblankDelay, int &hblankDelay) const override; + bool sensorEmbeddedDataPresent() const override; + +private: + /* + * Smallest difference between the frame length and integration time, + * in units of lines. + */ + static constexpr int frameIntegrationDiff = 32; + /* Maximum frame length allowable for long exposure calculations. */ + static constexpr int frameLengthMax = 0xffdc; + /* Largest long exposure scale factor given as a left shift on the frame length. */ + static constexpr int longExposureShiftMax = 7; + + void populateMetadata(const MdParser::RegisterMap ®isters, + Metadata &metadata) const override; +}; + +CamHelperImx519::CamHelperImx519() + : CamHelper(std::make_unique(registerList), frameIntegrationDiff) +{ +} + +uint32_t CamHelperImx519::gainCode(double gain) const +{ + return static_cast(1024 - 1024 / gain); +} + +double CamHelperImx519::gain(uint32_t gainCode) const +{ + return 1024.0 / (1024 - gainCode); +} + +void CamHelperImx519::prepare(libcamera::Span buffer, Metadata &metadata) +{ + MdParser::RegisterMap registers; + DeviceStatus deviceStatus; + + if (metadata.get("device.status", deviceStatus)) { + LOG(IPARPI, Error) << "DeviceStatus not found from DelayedControls"; + return; + } + + parseEmbeddedData(buffer, metadata); + + /* + * The DeviceStatus struct is first populated with values obtained from + * DelayedControls. If this reports frame length is > frameLengthMax, + * it means we are using a long exposure mode. Since the long exposure + * scale factor is not returned back through embedded data, we must rely + * on the existing exposure lines and frame length values returned by + * DelayedControls. + * + * Otherwise, all values are updated with what is reported in the + * embedded data. + */ + if (deviceStatus.frameLength > frameLengthMax) { + DeviceStatus parsedDeviceStatus; + + metadata.get("device.status", parsedDeviceStatus); + parsedDeviceStatus.shutterSpeed = deviceStatus.shutterSpeed; + parsedDeviceStatus.frameLength = deviceStatus.frameLength; + metadata.set("device.status", parsedDeviceStatus); + + LOG(IPARPI, Debug) << "Metadata updated for long exposure: " + << parsedDeviceStatus; + } +} + +std::pair CamHelperImx519::getBlanking(Duration &exposure, + Duration minFrameDuration, + Duration maxFrameDuration) const +{ + uint32_t frameLength, exposureLines; + unsigned int shift = 0; + + auto [vblank, hblank] = CamHelper::getBlanking(exposure, minFrameDuration, + maxFrameDuration); + + frameLength = mode_.height + vblank; + Duration lineLength = hblankToLineLength(hblank); + + /* + * Check if the frame length calculated needs to be setup for long + * exposure mode. This will require us to use a long exposure scale + * factor provided by a shift operation in the sensor. + */ + while (frameLength > frameLengthMax) { + if (++shift > longExposureShiftMax) { + shift = longExposureShiftMax; + frameLength = frameLengthMax; + break; + } + frameLength >>= 1; + } + + if (shift) { + /* Account for any rounding in the scaled frame length value. */ + frameLength <<= shift; + exposureLines = CamHelperImx519::exposureLines(exposure, lineLength); + exposureLines = std::min(exposureLines, frameLength - frameIntegrationDiff); + exposure = CamHelperImx519::exposure(exposureLines, lineLength); + } + + return { frameLength - mode_.height, hblank }; +} + +void CamHelperImx519::getDelays(int &exposureDelay, int &gainDelay, + int &vblankDelay, int &hblankDelay) const +{ + exposureDelay = 2; + gainDelay = 2; + vblankDelay = 3; + hblankDelay = 3; +} + +bool CamHelperImx519::sensorEmbeddedDataPresent() const +{ + return true; +} + +void CamHelperImx519::populateMetadata(const MdParser::RegisterMap ®isters, + Metadata &metadata) const +{ + DeviceStatus deviceStatus; + + deviceStatus.lineLength = lineLengthPckToDuration(registers.at(lineLengthHiReg) * 256 + + registers.at(lineLengthLoReg)); + deviceStatus.shutterSpeed = exposure(registers.at(expHiReg) * 256 + registers.at(expLoReg), + deviceStatus.lineLength); + deviceStatus.analogueGain = gain(registers.at(gainHiReg) * 256 + registers.at(gainLoReg)); + deviceStatus.frameLength = registers.at(frameLengthHiReg) * 256 + registers.at(frameLengthLoReg); + + metadata.set("device.status", deviceStatus); +} + +static CamHelper *create() +{ + return new CamHelperImx519(); +} + +static RegisterCamHelper reg("imx519", &create); diff --git a/src/ipa/rpi/cam_helper/cam_helper_imx708.cpp b/src/ipa/rpi/cam_helper/cam_helper_imx708.cpp new file mode 100644 index 00000000..641ba18f --- /dev/null +++ b/src/ipa/rpi/cam_helper/cam_helper_imx708.cpp @@ -0,0 +1,359 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (C) 2022, Raspberry Pi Ltd + * + * cam_helper_imx708.cpp - camera helper for imx708 sensor + */ + +#include +#include +#include +#include + +#include + +#include "controller/pdaf_data.h" + +#include "cam_helper.h" +#include "md_parser.h" + +using namespace RPiController; +using namespace libcamera; +using libcamera::utils::Duration; + +namespace libcamera { +LOG_DECLARE_CATEGORY(IPARPI) +} + +/* + * We care about two gain registers and a pair of exposure registers. Their + * I2C addresses from the Sony imx708 datasheet: + */ +constexpr uint32_t expHiReg = 0x0202; +constexpr uint32_t expLoReg = 0x0203; +constexpr uint32_t gainHiReg = 0x0204; +constexpr uint32_t gainLoReg = 0x0205; +constexpr uint32_t frameLengthHiReg = 0x0340; +constexpr uint32_t frameLengthLoReg = 0x0341; +constexpr uint32_t lineLengthHiReg = 0x0342; +constexpr uint32_t lineLengthLoReg = 0x0343; +constexpr uint32_t temperatureReg = 0x013a; +constexpr std::initializer_list registerList = + { expHiReg, expLoReg, gainHiReg, gainLoReg, lineLengthHiReg, + lineLengthLoReg, frameLengthHiReg, frameLengthLoReg, temperatureReg }; + +class CamHelperImx708 : public CamHelper +{ +public: + CamHelperImx708(); + uint32_t gainCode(double gain) const override; + double gain(uint32_t gain_code) const override; + void prepare(libcamera::Span buffer, Metadata &metadata) override; + void process(StatisticsPtr &stats, Metadata &metadata) override; + std::pair getBlanking(Duration &exposure, Duration minFrameDuration, + Duration maxFrameDuration) const override; + void getDelays(int &exposureDelay, int &gainDelay, + int &vblankDelay, int &hblankDelay) const override; + bool sensorEmbeddedDataPresent() const override; + double getModeSensitivity(const CameraMode &mode) const override; + unsigned int hideFramesModeSwitch() const override { return 1; } // seems to be required for HDR + +private: + /* + * Smallest difference between the frame length and integration time, + * in units of lines. + */ + static constexpr int frameIntegrationDiff = 22; + /* Maximum frame length allowable for long exposure calculations. */ + static constexpr int frameLengthMax = 0xffdc; + /* Largest long exposure scale factor given as a left shift on the frame length. */ + static constexpr int longExposureShiftMax = 7; + + static constexpr int pdafStatsRows = 12; + static constexpr int pdafStatsCols = 16; + + void populateMetadata(const MdParser::RegisterMap ®isters, + Metadata &metadata) const override; + + static bool parsePdafData(const uint8_t *ptr, size_t len, unsigned bpp, + PdafRegions &pdaf); + + bool parseAEHist(const uint8_t *ptr, size_t len, unsigned bpp); + void putAGCStatistics(StatisticsPtr stats); + + Histogram aeHistLinear_; + uint32_t aeHistAverage_; + bool aeHistValid_; +}; + +CamHelperImx708::CamHelperImx708() + : CamHelper(std::make_unique(registerList), frameIntegrationDiff), + aeHistLinear_{}, aeHistAverage_(0), aeHistValid_(false) +{ +} + +uint32_t CamHelperImx708::gainCode(double gain) const +{ + return static_cast(1024 - 1024 / gain); +} + +double CamHelperImx708::gain(uint32_t gain_code) const +{ + return 1024.0 / (1024 - gain_code); +} + +void CamHelperImx708::prepare(libcamera::Span buffer, Metadata &metadata) +{ + MdParser::RegisterMap registers; + DeviceStatus deviceStatus; + + LOG(IPARPI, Debug) << "Embedded buffer size: " << buffer.size(); + + if (metadata.get("device.status", deviceStatus)) { + LOG(IPARPI, Error) << "DeviceStatus not found from DelayedControls"; + return; + } + + parseEmbeddedData(buffer, metadata); + + /* + * Parse PDAF data, which we expect to occupy the third scanline + * of embedded data. As PDAF is quite sensor-specific, it's parsed here. + */ + size_t bytesPerLine = (mode_.width * mode_.bitdepth) >> 3; + + if (buffer.size() > 2 * bytesPerLine) { + PdafRegions pdaf; + if (parsePdafData(&buffer[2 * bytesPerLine], + buffer.size() - 2 * bytesPerLine, + mode_.bitdepth, pdaf)) + metadata.set("pdaf.regions", pdaf); + } + + /* Parse AE-HIST data where present */ + if (buffer.size() > 3 * bytesPerLine) { + aeHistValid_ = parseAEHist(&buffer[3 * bytesPerLine], + buffer.size() - 3 * bytesPerLine, + mode_.bitdepth); + } + + /* + * The DeviceStatus struct is first populated with values obtained from + * DelayedControls. If this reports frame length is > frameLengthMax, + * it means we are using a long exposure mode. Since the long exposure + * scale factor is not returned back through embedded data, we must rely + * on the existing exposure lines and frame length values returned by + * DelayedControls. + * + * Otherwise, all values are updated with what is reported in the + * embedded data. + */ + if (deviceStatus.frameLength > frameLengthMax) { + DeviceStatus parsedDeviceStatus; + + metadata.get("device.status", parsedDeviceStatus); + parsedDeviceStatus.shutterSpeed = deviceStatus.shutterSpeed; + parsedDeviceStatus.frameLength = deviceStatus.frameLength; + metadata.set("device.status", parsedDeviceStatus); + + LOG(IPARPI, Debug) << "Metadata updated for long exposure: " + << parsedDeviceStatus; + } +} + +void CamHelperImx708::process(StatisticsPtr &stats, [[maybe_unused]] Metadata &metadata) +{ + if (aeHistValid_) + putAGCStatistics(stats); +} + +std::pair CamHelperImx708::getBlanking(Duration &exposure, + Duration minFrameDuration, + Duration maxFrameDuration) const +{ + uint32_t frameLength, exposureLines; + unsigned int shift = 0; + + auto [vblank, hblank] = CamHelper::getBlanking(exposure, minFrameDuration, + maxFrameDuration); + + frameLength = mode_.height + vblank; + Duration lineLength = hblankToLineLength(hblank); + + /* + * Check if the frame length calculated needs to be setup for long + * exposure mode. This will require us to use a long exposure scale + * factor provided by a shift operation in the sensor. + */ + while (frameLength > frameLengthMax) { + if (++shift > longExposureShiftMax) { + shift = longExposureShiftMax; + frameLength = frameLengthMax; + break; + } + frameLength >>= 1; + } + + if (shift) { + /* Account for any rounding in the scaled frame length value. */ + frameLength <<= shift; + exposureLines = CamHelper::exposureLines(exposure, lineLength); + exposureLines = std::min(exposureLines, frameLength - frameIntegrationDiff); + exposure = CamHelper::exposure(exposureLines, lineLength); + } + + return { frameLength - mode_.height, hblank }; +} + +void CamHelperImx708::getDelays(int &exposureDelay, int &gainDelay, + int &vblankDelay, int &hblankDelay) const +{ + exposureDelay = 2; + gainDelay = 2; + vblankDelay = 3; + hblankDelay = 3; +} + +bool CamHelperImx708::sensorEmbeddedDataPresent() const +{ + return true; +} + +double CamHelperImx708::getModeSensitivity(const CameraMode &mode) const +{ + /* In binned modes, sensitivity increases by a factor of 2 */ + return (mode.width > 2304) ? 1.0 : 2.0; +} + +void CamHelperImx708::populateMetadata(const MdParser::RegisterMap ®isters, + Metadata &metadata) const +{ + DeviceStatus deviceStatus; + + deviceStatus.lineLength = lineLengthPckToDuration(registers.at(lineLengthHiReg) * 256 + + registers.at(lineLengthLoReg)); + deviceStatus.shutterSpeed = exposure(registers.at(expHiReg) * 256 + registers.at(expLoReg), + deviceStatus.lineLength); + deviceStatus.analogueGain = gain(registers.at(gainHiReg) * 256 + registers.at(gainLoReg)); + deviceStatus.frameLength = registers.at(frameLengthHiReg) * 256 + registers.at(frameLengthLoReg); + deviceStatus.sensorTemperature = std::clamp(registers.at(temperatureReg), -20, 80); + + metadata.set("device.status", deviceStatus); +} + +bool CamHelperImx708::parsePdafData(const uint8_t *ptr, size_t len, + unsigned bpp, PdafRegions &pdaf) +{ + size_t step = bpp >> 1; /* bytes per PDAF grid entry */ + + if (bpp < 10 || bpp > 12 || len < 194 * step || ptr[0] != 0 || ptr[1] >= 0x40) { + LOG(IPARPI, Error) << "PDAF data in unsupported format"; + return false; + } + + pdaf.init({ pdafStatsCols, pdafStatsRows }); + + ptr += 2 * step; + for (unsigned i = 0; i < pdafStatsRows; ++i) { + for (unsigned j = 0; j < pdafStatsCols; ++j) { + unsigned c = (ptr[0] << 3) | (ptr[1] >> 5); + int p = (((ptr[1] & 0x0F) - (ptr[1] & 0x10)) << 6) | (ptr[2] >> 2); + PdafData pdafData; + pdafData.conf = c; + pdafData.phase = c ? p : 0; + pdaf.set(libcamera::Point(j, i), { pdafData, 1, 0 }); + ptr += step; + } + } + + return true; +} + +bool CamHelperImx708::parseAEHist(const uint8_t *ptr, size_t len, unsigned bpp) +{ + static constexpr unsigned int PipelineBits = Statistics::NormalisationFactorPow2; + + uint64_t count = 0, sum = 0; + size_t step = bpp >> 1; /* bytes per histogram bin */ + uint32_t hist[128]; + + if (len < 144 * step) + return false; + + /* + * Read the 128 bin linear histogram, which by default covers + * the full range of the HDR shortest exposure (small values are + * expected to dominate, so pixel-value resolution will be poor). + */ + for (unsigned i = 0; i < 128; ++i) { + if (ptr[3] != 0x55) + return false; + uint32_t c = (ptr[0] << 14) + (ptr[1] << 6) + (ptr[2] >> 2); + hist[i] = c >> 2; /* pixels to quads */ + if (i != 0) { + count += c; + sum += c * + (i * (1u << (PipelineBits - 7)) + + (1u << (PipelineBits - 8))); + } + ptr += step; + } + + /* + * Now use the first 9 bins of the log histogram (these should be + * subdivisions of the smallest linear bin), to get a more accurate + * average value. Don't assume that AEHIST1_AVERAGE is present. + */ + for (unsigned i = 0; i < 9; ++i) { + if (ptr[3] != 0x55) + return false; + uint32_t c = (ptr[0] << 14) + (ptr[1] << 6) + (ptr[2] >> 2); + count += c; + sum += c * + ((3u << PipelineBits) >> (17 - i)); + ptr += step; + } + if ((unsigned)((ptr[0] << 12) + (ptr[1] << 4) + (ptr[2] >> 4)) != + hist[1]) { + LOG(IPARPI, Error) << "Lin/Log histogram mismatch"; + return false; + } + + aeHistLinear_ = Histogram(hist, 128); + aeHistAverage_ = count ? (sum / count) : 0; + + return count != 0; +} + +void CamHelperImx708::putAGCStatistics(StatisticsPtr stats) +{ + /* + * For HDR mode, copy sensor's AE/AGC statistics over ISP's, so the + * AGC algorithm sees a linear response to exposure and gain changes. + * + * Histogram: Just copy the "raw" histogram over the tone-mapped one, + * although they have different distributions (raw values are lower). + * Tuning should either ignore it, or constrain for highlights only. + * + * Average: Overwrite all regional averages with a global raw average, + * scaled by a fiddle-factor so that a conventional (non-HDR) y_target + * of e.g. 0.17 will map to a suitable level for HDR. + */ + stats->yHist = aeHistLinear_; + + constexpr unsigned int HdrHeadroomFactor = 4; + uint64_t v = HdrHeadroomFactor * aeHistAverage_; + for (auto ®ion : stats->agcRegions) { + region.val.rSum = region.val.gSum = region.val.bSum = region.counted * v; + } +} + +static CamHelper *create() +{ + return new CamHelperImx708(); +} + +static RegisterCamHelper reg("imx708", &create); +static RegisterCamHelper regWide("imx708_wide", &create); +static RegisterCamHelper regNoIr("imx708_noir", &create); +static RegisterCamHelper regWideNoIr("imx708_wide_noir", &create); diff --git a/src/ipa/rpi/cam_helper/cam_helper_ov5647.cpp b/src/ipa/rpi/cam_helper/cam_helper_ov5647.cpp new file mode 100644 index 00000000..5a99083d --- /dev/null +++ b/src/ipa/rpi/cam_helper/cam_helper_ov5647.cpp @@ -0,0 +1,109 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (C) 2019, Raspberry Pi Ltd + * + * cam_helper_ov5647.cpp - camera information for ov5647 sensor + */ + +#include + +#include "cam_helper.h" + +using namespace RPiController; + +class CamHelperOv5647 : public CamHelper +{ +public: + CamHelperOv5647(); + uint32_t gainCode(double gain) const override; + double gain(uint32_t gainCode) const override; + void getDelays(int &exposureDelay, int &gainDelay, + int &vblankDelay, int &hblankDelay) const override; + unsigned int hideFramesStartup() const override; + unsigned int hideFramesModeSwitch() const override; + unsigned int mistrustFramesStartup() const override; + unsigned int mistrustFramesModeSwitch() const override; + +private: + /* + * Smallest difference between the frame length and integration time, + * in units of lines. + */ + static constexpr int frameIntegrationDiff = 4; +}; + +/* + * OV5647 doesn't output metadata, so we have to use the "unicam parser" which + * works by counting frames. + */ + +CamHelperOv5647::CamHelperOv5647() + : CamHelper({}, frameIntegrationDiff) +{ +} + +uint32_t CamHelperOv5647::gainCode(double gain) const +{ + return static_cast(gain * 16.0); +} + +double CamHelperOv5647::gain(uint32_t gainCode) const +{ + return static_cast(gainCode) / 16.0; +} + +void CamHelperOv5647::getDelays(int &exposureDelay, int &gainDelay, + int &vblankDelay, int &hblankDelay) const +{ + /* + * We run this sensor in a mode where the gain delay is bumped up to + * 2. It seems to be the only way to make the delays "predictable". + */ + exposureDelay = 2; + gainDelay = 2; + vblankDelay = 2; + hblankDelay = 2; +} + +unsigned int CamHelperOv5647::hideFramesStartup() const +{ + /* + * On startup, we get a couple of under-exposed frames which + * we don't want shown. + */ + return 2; +} + +unsigned int CamHelperOv5647::hideFramesModeSwitch() const +{ + /* + * After a mode switch, we get a couple of under-exposed frames which + * we don't want shown. + */ + return 2; +} + +unsigned int CamHelperOv5647::mistrustFramesStartup() const +{ + /* + * First couple of frames are under-exposed and are no good for control + * algos. + */ + return 2; +} + +unsigned int CamHelperOv5647::mistrustFramesModeSwitch() const +{ + /* + * First couple of frames are under-exposed even after a simple + * mode switch, and are no good for control algos. + */ + return 2; +} + +static CamHelper *create() +{ + return new CamHelperOv5647(); +} + +static RegisterCamHelper reg("ov5647", &create); diff --git a/src/ipa/rpi/cam_helper/cam_helper_ov9281.cpp b/src/ipa/rpi/cam_helper/cam_helper_ov9281.cpp new file mode 100644 index 00000000..86c5bc4c --- /dev/null +++ b/src/ipa/rpi/cam_helper/cam_helper_ov9281.cpp @@ -0,0 +1,66 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (C) 2021, Raspberry Pi Ltd + * + * cam_helper_ov9281.cpp - camera information for ov9281 sensor + */ + +#include + +#include "cam_helper.h" + +using namespace RPiController; + +class CamHelperOv9281 : public CamHelper +{ +public: + CamHelperOv9281(); + uint32_t gainCode(double gain) const override; + double gain(uint32_t gainCode) const override; + void getDelays(int &exposureDelay, int &gainDelay, + int &vblankDelay, int &hblankDelay) const override; + +private: + /* + * Smallest difference between the frame length and integration time, + * in units of lines. + */ + static constexpr int frameIntegrationDiff = 4; +}; + +/* + * OV9281 doesn't output metadata, so we have to use the "unicam parser" which + * works by counting frames. + */ + +CamHelperOv9281::CamHelperOv9281() + : CamHelper({}, frameIntegrationDiff) +{ +} + +uint32_t CamHelperOv9281::gainCode(double gain) const +{ + return static_cast(gain * 16.0); +} + +double CamHelperOv9281::gain(uint32_t gainCode) const +{ + return static_cast(gainCode) / 16.0; +} + +void CamHelperOv9281::getDelays(int &exposureDelay, int &gainDelay, + int &vblankDelay, int &hblankDelay) const +{ + /* The driver appears to behave as follows: */ + exposureDelay = 2; + gainDelay = 2; + vblankDelay = 2; + hblankDelay = 2; +} + +static CamHelper *create() +{ + return new CamHelperOv9281(); +} + +static RegisterCamHelper reg("ov9281", &create); diff --git a/src/ipa/rpi/cam_helper/md_parser.h b/src/ipa/rpi/cam_helper/md_parser.h new file mode 100644 index 00000000..77d557aa --- /dev/null +++ b/src/ipa/rpi/cam_helper/md_parser.h @@ -0,0 +1,155 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (C) 2019, Raspberry Pi Ltd + * + * md_parser.h - image sensor metadata parser interface + */ +#pragma once + +#include +#include +#include +#include + +#include + +/* + * Camera metadata parser class. Usage as shown below. + * + * Setup: + * + * Usually the metadata parser will be made as part of the CamHelper class so + * application code doesn't have to worry which kind to instantiate. But for + * the sake of example let's suppose we're parsing imx219 metadata. + * + * MdParser *parser = new MdParserSmia({ expHiReg, expLoReg, gainReg }); + * parser->SetBitsPerPixel(bpp); + * parser->SetLineLengthBytes(pitch); + * parser->SetNumLines(2); + * + * Note 1: if you don't know how many lines there are, the size of the input + * buffer is used as a limit instead. + * + * Note 2: if you don't know the line length, you can leave the line length unset + * (or set to zero) and the parser will hunt for the line start instead. + * + * Then on every frame: + * + * RegisterMap registers; + * if (parser->Parse(buffer, registers) != MdParser::OK) + * much badness; + * Metadata metadata; + * CamHelper::PopulateMetadata(registers, metadata); + * + * (Note that the CamHelper class converts to/from exposure lines and time, + * and gain_code / actual gain.) + * + * If you suspect your embedded data may have changed its layout, change any line + * lengths, number of lines, bits per pixel etc. that are different, and + * then: + * + * parser->Reset(); + * + * before calling Parse again. + */ + +namespace RPiController { + +/* Abstract base class from which other metadata parsers are derived. */ + +class MdParser +{ +public: + using RegisterMap = std::map; + + /* + * Parser status codes: + * OK - success + * NOTFOUND - value such as exposure or gain was not found + * ERROR - all other errors + */ + enum Status { + OK = 0, + NOTFOUND = 1, + ERROR = 2 + }; + + MdParser() + : reset_(true), bitsPerPixel_(0), numLines_(0), lineLengthBytes_(0) + { + } + + virtual ~MdParser() = default; + + void reset() + { + reset_ = true; + } + + void setBitsPerPixel(int bpp) + { + bitsPerPixel_ = bpp; + } + + void setNumLines(unsigned int numLines) + { + numLines_ = numLines; + } + + void setLineLengthBytes(unsigned int numBytes) + { + lineLengthBytes_ = numBytes; + } + + virtual Status parse(libcamera::Span buffer, + RegisterMap ®isters) = 0; + +protected: + bool reset_; + int bitsPerPixel_; + unsigned int numLines_; + unsigned int lineLengthBytes_; +}; + +/* + * This isn't a full implementation of a metadata parser for SMIA sensors, + * however, it does provide the findRegs function which will prove useful and + * make it easier to implement parsers for other SMIA-like sensors (see + * md_parser_imx219.cpp for an example). + */ + +class MdParserSmia final : public MdParser +{ +public: + MdParserSmia(std::initializer_list registerList); + + MdParser::Status parse(libcamera::Span buffer, + RegisterMap ®isters) override; + +private: + /* Maps register address to offset in the buffer. */ + using OffsetMap = std::map>; + + /* + * Note that error codes > 0 are regarded as non-fatal; codes < 0 + * indicate a bad data buffer. Status codes are: + * ParseOk - found all registers, much happiness + * MissingRegs - some registers found; should this be a hard error? + * The remaining codes are all hard errors. + */ + enum ParseStatus { + ParseOk = 0, + MissingRegs = 1, + NoLineStart = -1, + IllegalTag = -2, + BadDummy = -3, + BadLineEnd = -4, + BadPadding = -5 + }; + + ParseStatus findRegs(libcamera::Span buffer); + + OffsetMap offsets_; +}; + +} /* namespace RPi */ diff --git a/src/ipa/rpi/cam_helper/md_parser_smia.cpp b/src/ipa/rpi/cam_helper/md_parser_smia.cpp new file mode 100644 index 00000000..210787ed --- /dev/null +++ b/src/ipa/rpi/cam_helper/md_parser_smia.cpp @@ -0,0 +1,149 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (C) 2019-2021, Raspberry Pi Ltd + * + * md_parser_smia.cpp - SMIA specification based embedded data parser + */ + +#include +#include "md_parser.h" + +using namespace RPiController; +using namespace libcamera; + +/* + * This function goes through the embedded data to find the offsets (not + * values!), in the data block, where the values of the given registers can + * subsequently be found. + * + * Embedded data tag bytes, from Sony IMX219 datasheet but general to all SMIA + * sensors, I think. + */ + +constexpr unsigned int LineStart = 0x0a; +constexpr unsigned int LineEndTag = 0x07; +constexpr unsigned int RegHiBits = 0xaa; +constexpr unsigned int RegLowBits = 0xa5; +constexpr unsigned int RegValue = 0x5a; +constexpr unsigned int RegSkip = 0x55; + +MdParserSmia::MdParserSmia(std::initializer_list registerList) +{ + for (auto r : registerList) + offsets_[r] = {}; +} + +MdParser::Status MdParserSmia::parse(libcamera::Span buffer, + RegisterMap ®isters) +{ + if (reset_) { + /* + * Search again through the metadata for all the registers + * requested. + */ + ASSERT(bitsPerPixel_); + + for (const auto &kv : offsets_) + offsets_[kv.first] = {}; + + ParseStatus ret = findRegs(buffer); + /* + * > 0 means "worked partially but parse again next time", + * < 0 means "hard error". + * + * In either case, we retry parsing on the next frame. + */ + if (ret != ParseOk) + return ERROR; + + reset_ = false; + } + + /* Populate the register values requested. */ + registers.clear(); + for (const auto &[reg, offset] : offsets_) { + if (!offset) { + reset_ = true; + return NOTFOUND; + } + registers[reg] = buffer[offset.value()]; + } + + return OK; +} + +MdParserSmia::ParseStatus MdParserSmia::findRegs(libcamera::Span buffer) +{ + ASSERT(offsets_.size()); + + if (buffer[0] != LineStart) + return NoLineStart; + + unsigned int currentOffset = 1; /* after the LineStart */ + unsigned int currentLineStart = 0, currentLine = 0; + unsigned int regNum = 0, regsDone = 0; + + while (1) { + int tag = buffer[currentOffset++]; + + if ((bitsPerPixel_ == 10 && + (currentOffset + 1 - currentLineStart) % 5 == 0) || + (bitsPerPixel_ == 12 && + (currentOffset + 1 - currentLineStart) % 3 == 0)) { + if (buffer[currentOffset++] != RegSkip) + return BadDummy; + } + + int dataByte = buffer[currentOffset++]; + + if (tag == LineEndTag) { + if (dataByte != LineEndTag) + return BadLineEnd; + + if (numLines_ && ++currentLine == numLines_) + return MissingRegs; + + if (lineLengthBytes_) { + currentOffset = currentLineStart + lineLengthBytes_; + + /* Require whole line to be in the buffer (if buffer size set). */ + if (buffer.size() && + currentOffset + lineLengthBytes_ > buffer.size()) + return MissingRegs; + + if (buffer[currentOffset] != LineStart) + return NoLineStart; + } else { + /* allow a zero line length to mean "hunt for the next line" */ + while (currentOffset < buffer.size() && + buffer[currentOffset] != LineStart) + currentOffset++; + + if (currentOffset == buffer.size()) + return NoLineStart; + } + + /* inc currentOffset to after LineStart */ + currentLineStart = currentOffset++; + } else { + if (tag == RegHiBits) + regNum = (regNum & 0xff) | (dataByte << 8); + else if (tag == RegLowBits) + regNum = (regNum & 0xff00) | dataByte; + else if (tag == RegSkip) + regNum++; + else if (tag == RegValue) { + auto reg = offsets_.find(regNum); + + if (reg != offsets_.end()) { + offsets_[regNum] = currentOffset - 1; + + if (++regsDone == offsets_.size()) + return ParseOk; + } + regNum++; + } else + return IllegalTag; + } + } +} diff --git a/src/ipa/rpi/cam_helper/meson.build b/src/ipa/rpi/cam_helper/meson.build new file mode 100644 index 00000000..bdf2db8e --- /dev/null +++ b/src/ipa/rpi/cam_helper/meson.build @@ -0,0 +1,26 @@ +# SPDX-License-Identifier: CC0-1.0 + +rpi_ipa_cam_helper_sources = files([ + 'cam_helper.cpp', + 'cam_helper_ov5647.cpp', + 'cam_helper_imx219.cpp', + 'cam_helper_imx290.cpp', + 'cam_helper_imx296.cpp', + 'cam_helper_imx477.cpp', + 'cam_helper_imx519.cpp', + 'cam_helper_imx708.cpp', + 'cam_helper_ov9281.cpp', + 'md_parser_smia.cpp', +]) + +rpi_ipa_cam_helper_includes = [ + include_directories('..'), +] + +rpi_ipa_cam_helper_deps = [ + libcamera_private, +] + +rpi_ipa_cam_helper_lib = static_library('rpi_ipa_cam_helper', rpi_ipa_cam_helper_sources, + include_directories : rpi_ipa_cam_helper_includes, + dependencies : rpi_ipa_cam_helper_deps) diff --git a/src/ipa/rpi/controller/af_algorithm.h b/src/ipa/rpi/controller/af_algorithm.h new file mode 100644 index 00000000..ad9b5754 --- /dev/null +++ b/src/ipa/rpi/controller/af_algorithm.h @@ -0,0 +1,76 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (C) 2022, Raspberry Pi Ltd + * + * af_algorithm.hpp - auto focus algorithm interface + */ +#pragma once + +#include + +#include + +#include "algorithm.h" + +namespace RPiController { + +class AfAlgorithm : public Algorithm +{ +public: + AfAlgorithm(Controller *controller) + : Algorithm(controller) {} + + /* + * An autofocus algorithm should provide the following calls. + * + * Where a ControlList combines a change of AfMode with other AF + * controls, setMode() should be called first, to ensure the + * algorithm will be in the correct state to handle controls. + * + * setLensPosition() returns true if the mode was AfModeManual and + * the lens position has changed, otherwise returns false. When it + * returns true, hwpos should be sent immediately to the lens driver. + * + * getMode() is provided mainly for validating controls. + * getLensPosition() is provided for populating DeviceStatus. + */ + + enum AfRange { AfRangeNormal = 0, + AfRangeMacro, + AfRangeFull, + AfRangeMax }; + + enum AfSpeed { AfSpeedNormal = 0, + AfSpeedFast, + AfSpeedMax }; + + enum AfMode { AfModeManual = 0, + AfModeAuto, + AfModeContinuous }; + + enum AfPause { AfPauseImmediate = 0, + AfPauseDeferred, + AfPauseResume }; + + virtual void setRange([[maybe_unused]] AfRange range) + { + } + virtual void setSpeed([[maybe_unused]] AfSpeed speed) + { + } + virtual void setMetering([[maybe_unused]] bool use_windows) + { + } + virtual void setWindows([[maybe_unused]] libcamera::Span const &wins) + { + } + virtual void setMode(AfMode mode) = 0; + virtual AfMode getMode() const = 0; + virtual bool setLensPosition(double dioptres, int32_t *hwpos) = 0; + virtual std::optional getLensPosition() const = 0; + virtual void triggerScan() = 0; + virtual void cancelScan() = 0; + virtual void pause(AfPause pause) = 0; +}; + +} // namespace RPiController diff --git a/src/ipa/rpi/controller/af_status.h b/src/ipa/rpi/controller/af_status.h new file mode 100644 index 00000000..92c08812 --- /dev/null +++ b/src/ipa/rpi/controller/af_status.h @@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (C) 2022, Raspberry Pi Ltd + * + * af_status.h - AF control algorithm status + */ +#pragma once + +#include + +/* + * The AF algorithm should post the following structure into the image's + * "af.status" metadata. lensSetting should control the lens. + */ + +enum class AfState { + Idle = 0, + Scanning, + Focused, + Failed +}; + +enum class AfPauseState { + Running = 0, + Pausing, + Paused +}; + +struct AfStatus { + /* state for reporting */ + AfState state; + AfPauseState pauseState; + /* lensSetting should be sent to the lens driver, when valid */ + std::optional lensSetting; +}; diff --git a/src/ipa/rpi/controller/agc_algorithm.h b/src/ipa/rpi/controller/agc_algorithm.h new file mode 100644 index 00000000..36e6c110 --- /dev/null +++ b/src/ipa/rpi/controller/agc_algorithm.h @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (C) 2019, Raspberry Pi Ltd + * + * agc_algorithm.h - AGC/AEC control algorithm interface + */ +#pragma once + +#include + +#include "algorithm.h" + +namespace RPiController { + +class AgcAlgorithm : public Algorithm +{ +public: + AgcAlgorithm(Controller *controller) : Algorithm(controller) {} + /* An AGC algorithm must provide the following: */ + virtual unsigned int getConvergenceFrames() const = 0; + virtual void setEv(double ev) = 0; + virtual void setFlickerPeriod(libcamera::utils::Duration flickerPeriod) = 0; + virtual void setFixedShutter(libcamera::utils::Duration fixedShutter) = 0; + virtual void setMaxShutter(libcamera::utils::Duration maxShutter) = 0; + virtual void setFixedAnalogueGain(double fixedAnalogueGain) = 0; + virtual void setMeteringMode(std::string const &meteringModeName) = 0; + virtual void setExposureMode(std::string const &exposureModeName) = 0; + virtual void setConstraintMode(std::string const &contraintModeName) = 0; + virtual void enableAuto() = 0; + virtual void disableAuto() = 0; +}; + +} /* namespace RPiController */ diff --git a/src/ipa/rpi/controller/agc_status.h b/src/ipa/rpi/controller/agc_status.h new file mode 100644 index 00000000..6abf09d9 --- /dev/null +++ b/src/ipa/rpi/controller/agc_status.h @@ -0,0 +1,37 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (C) 2019, Raspberry Pi Ltd + * + * agc_status.h - AGC/AEC control algorithm status + */ +#pragma once + +#include + +/* + * The AGC algorithm should post the following structure into the image's + * "agc.status" metadata. + */ + +/* + * Note: total_exposure_value will be reported as zero until the algorithm has + * seen statistics and calculated meaningful values. The contents should be + * ignored until then. + */ + +struct AgcStatus { + libcamera::utils::Duration totalExposureValue; /* value for all exposure and gain for this image */ + libcamera::utils::Duration targetExposureValue; /* (unfiltered) target total exposure AGC is aiming for */ + libcamera::utils::Duration shutterTime; + double analogueGain; + char exposureMode[32]; + char constraintMode[32]; + char meteringMode[32]; + double ev; + libcamera::utils::Duration flickerPeriod; + int floatingRegionEnable; + libcamera::utils::Duration fixedShutter; + double fixedAnalogueGain; + double digitalGain; + int locked; +}; diff --git a/src/ipa/rpi/controller/algorithm.cpp b/src/ipa/rpi/controller/algorithm.cpp new file mode 100644 index 00000000..a957fde5 --- /dev/null +++ b/src/ipa/rpi/controller/algorithm.cpp @@ -0,0 +1,56 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (C) 2019, Raspberry Pi Ltd + * + * algorithm.cpp - ISP control algorithms + */ + +#include "algorithm.h" + +using namespace RPiController; + +int Algorithm::read([[maybe_unused]] const libcamera::YamlObject ¶ms) +{ + return 0; +} + +void Algorithm::initialise() +{ +} + +void Algorithm::switchMode([[maybe_unused]] CameraMode const &cameraMode, + [[maybe_unused]] Metadata *metadata) +{ +} + +void Algorithm::prepare([[maybe_unused]] Metadata *imageMetadata) +{ +} + +void Algorithm::process([[maybe_unused]] StatisticsPtr &stats, + [[maybe_unused]] Metadata *imageMetadata) +{ +} + +/* For registering algorithms with the system: */ + +namespace { + +std::map &algorithms() +{ + static std::map algorithms; + return algorithms; +} + +} /* namespace */ + +std::map const &RPiController::getAlgorithms() +{ + return algorithms(); +} + +RegisterAlgorithm::RegisterAlgorithm(char const *name, + AlgoCreateFunc createFunc) +{ + algorithms()[std::string(name)] = createFunc; +} diff --git a/src/ipa/rpi/controller/algorithm.h b/src/ipa/rpi/controller/algorithm.h new file mode 100644 index 00000000..4aa814eb --- /dev/null +++ b/src/ipa/rpi/controller/algorithm.h @@ -0,0 +1,68 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (C) 2019, Raspberry Pi Ltd + * + * algorithm.h - ISP control algorithm interface + */ +#pragma once + +/* + * All algorithms should be derived from this class and made available to the + * Controller. + */ + +#include +#include +#include + +#include "libcamera/internal/yaml_parser.h" + +#include "controller.h" + +namespace RPiController { + +/* This defines the basic interface for all control algorithms. */ + +class Algorithm +{ +public: + Algorithm(Controller *controller) + : controller_(controller) + { + } + virtual ~Algorithm() = default; + virtual char const *name() const = 0; + virtual int read(const libcamera::YamlObject ¶ms); + virtual void initialise(); + virtual void switchMode(CameraMode const &cameraMode, Metadata *metadata); + virtual void prepare(Metadata *imageMetadata); + virtual void process(StatisticsPtr &stats, Metadata *imageMetadata); + Metadata &getGlobalMetadata() const + { + return controller_->getGlobalMetadata(); + } + const std::string &getTarget() const + { + return controller_->getTarget(); + } + const Controller::HardwareConfig &getHardwareConfig() const + { + return controller_->getHardwareConfig(); + } + +private: + Controller *controller_; +}; + +/* + * This code is for automatic registration of Front End algorithms with the + * system. + */ + +typedef Algorithm *(*AlgoCreateFunc)(Controller *controller); +struct RegisterAlgorithm { + RegisterAlgorithm(char const *name, AlgoCreateFunc createFunc); +}; +std::map const &getAlgorithms(); + +} /* namespace RPiController */ diff --git a/src/ipa/rpi/controller/alsc_status.h b/src/ipa/rpi/controller/alsc_status.h new file mode 100644 index 00000000..49a9f4a0 --- /dev/null +++ b/src/ipa/rpi/controller/alsc_status.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (C) 2019, Raspberry Pi Ltd + * + * alsc_status.h - ALSC (auto lens shading correction) control algorithm status + */ +#pragma once + +#include + +/* + * The ALSC algorithm should post the following structure into the image's + * "alsc.status" metadata. + */ + +struct AlscStatus { + std::vector r; + std::vector g; + std::vector b; + unsigned int rows; + unsigned int cols; +}; diff --git a/src/ipa/rpi/controller/awb_algorithm.h b/src/ipa/rpi/controller/awb_algorithm.h new file mode 100644 index 00000000..8462c4db --- /dev/null +++ b/src/ipa/rpi/controller/awb_algorithm.h @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (C) 2019, Raspberry Pi Ltd + * + * awb_algorithm.h - AWB control algorithm interface + */ +#pragma once + +#include "algorithm.h" + +namespace RPiController { + +class AwbAlgorithm : public Algorithm +{ +public: + AwbAlgorithm(Controller *controller) : Algorithm(controller) {} + /* An AWB algorithm must provide the following: */ + virtual unsigned int getConvergenceFrames() const = 0; + virtual void setMode(std::string const &modeName) = 0; + virtual void setManualGains(double manualR, double manualB) = 0; + virtual void enableAuto() = 0; + virtual void disableAuto() = 0; +}; + +} /* namespace RPiController */ diff --git a/src/ipa/rpi/controller/awb_status.h b/src/ipa/rpi/controller/awb_status.h new file mode 100644 index 00000000..dd5a79e3 --- /dev/null +++ b/src/ipa/rpi/controller/awb_status.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (C) 2019, Raspberry Pi Ltd + * + * awb_status.h - AWB control algorithm status + */ +#pragma once + +/* + * The AWB algorithm places its results into both the image and global metadata, + * under the tag "awb.status". + */ + +struct AwbStatus { + char mode[32]; + double temperatureK; + double gainR; + double gainG; + double gainB; +}; diff --git a/src/ipa/rpi/controller/black_level_status.h b/src/ipa/rpi/controller/black_level_status.h new file mode 100644 index 00000000..fd5e4ccb --- /dev/null +++ b/src/ipa/rpi/controller/black_level_status.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (C) 2019, Raspberry Pi Ltd + * + * black_level_status.h - black level control algorithm status + */ +#pragma once + +/* The "black level" algorithm stores the black levels to use. */ + +struct BlackLevelStatus { + uint16_t blackLevelR; /* out of 16 bits */ + uint16_t blackLevelG; + uint16_t blackLevelB; +}; diff --git a/src/ipa/rpi/controller/camera_mode.h b/src/ipa/rpi/controller/camera_mode.h new file mode 100644 index 00000000..63b11778 --- /dev/null +++ b/src/ipa/rpi/controller/camera_mode.h @@ -0,0 +1,59 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (C) 2019-2020, Raspberry Pi Ltd + * + * camera_mode.h - description of a particular operating mode of a sensor + */ +#pragma once + +#include + +#include + +/* + * Description of a "camera mode", holding enough information for control + * algorithms to adapt their behaviour to the different modes of the camera, + * including binning, scaling, cropping etc. + */ + +struct CameraMode { + /* bit depth of the raw camera output */ + uint32_t bitdepth; + /* size in pixels of frames in this mode */ + uint16_t width; + uint16_t height; + /* size of full resolution uncropped frame ("sensor frame") */ + uint16_t sensorWidth; + uint16_t sensorHeight; + /* binning factor (1 = no binning, 2 = 2-pixel binning etc.) */ + uint8_t binX; + uint8_t binY; + /* location of top left pixel in the sensor frame */ + uint16_t cropX; + uint16_t cropY; + /* scaling factor (so if uncropped, width*scaleX is sensorWidth) */ + double scaleX; + double scaleY; + /* scaling of the noise compared to the native sensor mode */ + double noiseFactor; + /* minimum and maximum line time and frame durations */ + libcamera::utils::Duration minLineLength; + libcamera::utils::Duration maxLineLength; + libcamera::utils::Duration minFrameDuration; + libcamera::utils::Duration maxFrameDuration; + /* any camera transform *not* reflected already in the camera tuning */ + libcamera::Transform transform; + /* minimum and maximum frame lengths in units of lines */ + uint32_t minFrameLength; + uint32_t maxFrameLength; + /* sensitivity of this mode */ + double sensitivity; + /* pixel clock rate */ + uint64_t pixelRate; + /* Mode specific shutter speed limits */ + libcamera::utils::Duration minShutter; + libcamera::utils::Duration maxShutter; + /* Mode specific analogue gain limits */ + double minAnalogueGain; + double maxAnalogueGain; +}; diff --git a/src/ipa/rpi/controller/ccm_algorithm.h b/src/ipa/rpi/controller/ccm_algorithm.h new file mode 100644 index 00000000..e2c4d771 --- /dev/null +++ b/src/ipa/rpi/controller/ccm_algorithm.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (C) 2019, Raspberry Pi Ltd + * + * ccm_algorithm.h - CCM (colour correction matrix) control algorithm interface + */ +#pragma once + +#include "algorithm.h" + +namespace RPiController { + +class CcmAlgorithm : public Algorithm +{ +public: + CcmAlgorithm(Controller *controller) : Algorithm(controller) {} + /* A CCM algorithm must provide the following: */ + virtual void setSaturation(double saturation) = 0; +}; + +} /* namespace RPiController */ diff --git a/src/ipa/rpi/controller/ccm_status.h b/src/ipa/rpi/controller/ccm_status.h new file mode 100644 index 00000000..5e28ee7c --- /dev/null +++ b/src/ipa/rpi/controller/ccm_status.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (C) 2019, Raspberry Pi Ltd + * + * ccm_status.h - CCM (colour correction matrix) control algorithm status + */ +#pragma once + +/* The "ccm" algorithm generates an appropriate colour matrix. */ + +struct CcmStatus { + double matrix[9]; + double saturation; +}; diff --git a/src/ipa/rpi/controller/contrast_algorithm.h b/src/ipa/rpi/controller/contrast_algorithm.h new file mode 100644 index 00000000..ce17a4f9 --- /dev/null +++ b/src/ipa/rpi/controller/contrast_algorithm.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (C) 2019, Raspberry Pi Ltd + * + * contrast_algorithm.h - contrast (gamma) control algorithm interface + */ +#pragma once + +#include "algorithm.h" + +namespace RPiController { + +class ContrastAlgorithm : public Algorithm +{ +public: + ContrastAlgorithm(Controller *controller) : Algorithm(controller) {} + /* A contrast algorithm must provide the following: */ + virtual void setBrightness(double brightness) = 0; + virtual void setContrast(double contrast) = 0; +}; + +} /* namespace RPiController */ diff --git a/src/ipa/rpi/controller/contrast_status.h b/src/ipa/rpi/controller/contrast_status.h new file mode 100644 index 00000000..fb9fe4ba --- /dev/null +++ b/src/ipa/rpi/controller/contrast_status.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (C) 2019, Raspberry Pi Ltd + * + * contrast_status.h - contrast (gamma) control algorithm status + */ +#pragma once + +#include "pwl.h" + +/* + * The "contrast" algorithm creates a gamma curve, optionally doing a little bit + * of contrast stretching based on the AGC histogram. + */ + +struct ContrastStatus { + RPiController::Pwl gammaCurve; + double brightness; + double contrast; +}; diff --git a/src/ipa/rpi/controller/controller.cpp b/src/ipa/rpi/controller/controller.cpp new file mode 100644 index 00000000..fa172113 --- /dev/null +++ b/src/ipa/rpi/controller/controller.cpp @@ -0,0 +1,181 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (C) 2019, Raspberry Pi Ltd + * + * controller.cpp - ISP controller + */ + +#include + +#include +#include + +#include "libcamera/internal/yaml_parser.h" + +#include "algorithm.h" +#include "controller.h" + +using namespace RPiController; +using namespace libcamera; + +LOG_DEFINE_CATEGORY(RPiController) + +static const std::map HardwareConfigMap = { + { + "bcm2835", + { + /* + * There are only ever 15 AGC regions computed by the firmware + * due to zoning, but the HW defines AGC_REGIONS == 16! + */ + .agcRegions = { 15 , 1 }, + .agcZoneWeights = { 15 , 1 }, + .awbRegions = { 16, 12 }, + .focusRegions = { 4, 3 }, + .numHistogramBins = 128, + .numGammaPoints = 33, + .pipelineWidth = 13 + } + }, +}; + +Controller::Controller() + : switchModeCalled_(false) +{ +} + +Controller::~Controller() {} + +int Controller::read(char const *filename) +{ + File file(filename); + if (!file.open(File::OpenModeFlag::ReadOnly)) { + LOG(RPiController, Warning) + << "Failed to open tuning file '" << filename << "'"; + return -EINVAL; + } + + std::unique_ptr root = YamlParser::parse(file); + double version = (*root)["version"].get(1.0); + target_ = (*root)["target"].get("bcm2835"); + + if (version < 2.0) { + LOG(RPiController, Warning) + << "This format of the tuning file will be deprecated soon!" + << " Please use the convert_tuning.py utility to update to version 2.0."; + + for (auto const &[key, value] : root->asDict()) { + int ret = createAlgorithm(key, value); + if (ret) + return ret; + } + } else if (version < 3.0) { + if (!root->contains("algorithms")) { + LOG(RPiController, Error) + << "Tuning file " << filename + << " does not have an \"algorithms\" list!"; + return -EINVAL; + } + + for (auto const &rootAlgo : (*root)["algorithms"].asList()) + for (auto const &[key, value] : rootAlgo.asDict()) { + int ret = createAlgorithm(key, value); + if (ret) + return ret; + } + } else { + LOG(RPiController, Error) + << "Unrecognised version " << version + << " for the tuning file " << filename; + return -EINVAL; + } + + return 0; +} + +int Controller::createAlgorithm(const std::string &name, const YamlObject ¶ms) +{ + auto it = getAlgorithms().find(name); + if (it == getAlgorithms().end()) { + LOG(RPiController, Warning) + << "No algorithm found for \"" << name << "\""; + return 0; + } + + Algorithm *algo = (*it->second)(this); + int ret = algo->read(params); + if (ret) + return ret; + + algorithms_.push_back(AlgorithmPtr(algo)); + return 0; +} + +void Controller::initialise() +{ + for (auto &algo : algorithms_) + algo->initialise(); +} + +void Controller::switchMode(CameraMode const &cameraMode, Metadata *metadata) +{ + for (auto &algo : algorithms_) + algo->switchMode(cameraMode, metadata); + switchModeCalled_ = true; +} + +void Controller::prepare(Metadata *imageMetadata) +{ + assert(switchModeCalled_); + for (auto &algo : algorithms_) + algo->prepare(imageMetadata); +} + +void Controller::process(StatisticsPtr stats, Metadata *imageMetadata) +{ + assert(switchModeCalled_); + for (auto &algo : algorithms_) + algo->process(stats, imageMetadata); +} + +Metadata &Controller::getGlobalMetadata() +{ + return globalMetadata_; +} + +Algorithm *Controller::getAlgorithm(std::string const &name) const +{ + /* + * The passed name must be the entire algorithm name, or must match the + * last part of it with a period (.) just before. + */ + size_t nameLen = name.length(); + for (auto &algo : algorithms_) { + char const *algoName = algo->name(); + size_t algoNameLen = strlen(algoName); + if (algoNameLen >= nameLen && + strcasecmp(name.c_str(), + algoName + algoNameLen - nameLen) == 0 && + (nameLen == algoNameLen || + algoName[algoNameLen - nameLen - 1] == '.')) + return algo.get(); + } + return nullptr; +} + +const std::string &Controller::getTarget() const +{ + return target_; +} + +const Controller::HardwareConfig &Controller::getHardwareConfig() const +{ + auto cfg = HardwareConfigMap.find(getTarget()); + + /* + * This really should not happen, the IPA ought to validate the target + * on initialisation. + */ + ASSERT(cfg != HardwareConfigMap.end()); + return cfg->second; +} diff --git a/src/ipa/rpi/controller/controller.h b/src/ipa/rpi/controller/controller.h new file mode 100644 index 00000000..c6af5cd6 --- /dev/null +++ b/src/ipa/rpi/controller/controller.h @@ -0,0 +1,73 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (C) 2019, Raspberry Pi Ltd + * + * controller.h - ISP controller interface + */ +#pragma once + +/* + * The Controller is simply a container for a collecting together a number of + * "control algorithms" (such as AWB etc.) and for running them all in a + * convenient manner. + */ + +#include +#include + +#include "libcamera/internal/yaml_parser.h" + +#include "camera_mode.h" +#include "device_status.h" +#include "metadata.h" +#include "statistics.h" + +namespace RPiController { + +class Algorithm; +typedef std::unique_ptr AlgorithmPtr; + +/* + * The Controller holds a pointer to some global_metadata, which is how + * different controllers and control algorithms within them can exchange + * information. The Prepare function returns a pointer to metadata for this + * specific image, and which should be passed on to the Process function. + */ + +class Controller +{ +public: + struct HardwareConfig { + libcamera::Size agcRegions; + libcamera::Size agcZoneWeights; + libcamera::Size awbRegions; + libcamera::Size focusRegions; + unsigned int numHistogramBins; + unsigned int numGammaPoints; + unsigned int pipelineWidth; + }; + + Controller(); + ~Controller(); + int read(char const *filename); + void initialise(); + void switchMode(CameraMode const &cameraMode, Metadata *metadata); + void prepare(Metadata *imageMetadata); + void process(StatisticsPtr stats, Metadata *imageMetadata); + Metadata &getGlobalMetadata(); + Algorithm *getAlgorithm(std::string const &name) const; + const std::string &getTarget() const; + const HardwareConfig &getHardwareConfig() const; + +protected: + int createAlgorithm(const std::string &name, const libcamera::YamlObject ¶ms); + + Metadata globalMetadata_; + std::vector algorithms_; + bool switchModeCalled_; + +private: + std::string target_; +}; + +} /* namespace RPiController */ diff --git a/src/ipa/rpi/controller/denoise_algorithm.h b/src/ipa/rpi/controller/denoise_algorithm.h new file mode 100644 index 00000000..52009ba9 --- /dev/null +++ b/src/ipa/rpi/controller/denoise_algorithm.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (C) 2021, Raspberry Pi Ltd + * + * denoise.h - Denoise control algorithm interface + */ +#pragma once + +#include "algorithm.h" + +namespace RPiController { + +enum class DenoiseMode { Off, ColourOff, ColourFast, ColourHighQuality }; + +class DenoiseAlgorithm : public Algorithm +{ +public: + DenoiseAlgorithm(Controller *controller) : Algorithm(controller) {} + /* A Denoise algorithm must provide the following: */ + virtual void setMode(DenoiseMode mode) = 0; +}; + +} /* namespace RPiController */ diff --git a/src/ipa/rpi/controller/denoise_status.h b/src/ipa/rpi/controller/denoise_status.h new file mode 100644 index 00000000..f6b9ee29 --- /dev/null +++ b/src/ipa/rpi/controller/denoise_status.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (C) 2019-2021, Raspberry Pi Ltd + * + * denoise_status.h - Denoise control algorithm status + */ +#pragma once + +/* This stores the parameters required for Denoise. */ + +struct DenoiseStatus { + double noiseConstant; + double noiseSlope; + double strength; + unsigned int mode; +}; diff --git a/src/ipa/rpi/controller/device_status.cpp b/src/ipa/rpi/controller/device_status.cpp new file mode 100644 index 00000000..c907efdd --- /dev/null +++ b/src/ipa/rpi/controller/device_status.cpp @@ -0,0 +1,31 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (C) 2021, Raspberry Pi Ltd + * + * device_status.cpp - device (image sensor) status + */ +#include "device_status.h" + +using namespace libcamera; /* for the Duration operator<< overload */ + +std::ostream &operator<<(std::ostream &out, const DeviceStatus &d) +{ + out << "Exposure: " << d.shutterSpeed + << " Frame length: " << d.frameLength + << " Line length: " << d.lineLength + << " Gain: " << d.analogueGain; + + if (d.aperture) + out << " Aperture: " << *d.aperture; + + if (d.lensPosition) + out << " Lens: " << *d.lensPosition; + + if (d.flashIntensity) + out << " Flash: " << *d.flashIntensity; + + if (d.sensorTemperature) + out << " Temperature: " << *d.sensorTemperature; + + return out; +} diff --git a/src/ipa/rpi/controller/device_status.h b/src/ipa/rpi/controller/device_status.h new file mode 100644 index 00000000..c45db749 --- /dev/null +++ b/src/ipa/rpi/controller/device_status.h @@ -0,0 +1,43 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (C) 2019-2021, Raspberry Pi Ltd + * + * device_status.h - device (image sensor) status + */ +#pragma once + +#include +#include + +#include + +/* + * Definition of "device metadata" which stores things like shutter time and + * analogue gain that downstream control algorithms will want to know. + */ + +struct DeviceStatus { + DeviceStatus() + : shutterSpeed(std::chrono::seconds(0)), frameLength(0), + lineLength(std::chrono::seconds(0)), analogueGain(0.0) + { + } + + friend std::ostream &operator<<(std::ostream &out, const DeviceStatus &d); + + /* time shutter is open */ + libcamera::utils::Duration shutterSpeed; + /* frame length given in number of lines */ + uint32_t frameLength; + /* line length for the current frame */ + libcamera::utils::Duration lineLength; + double analogueGain; + /* 1.0/distance-in-metres */ + std::optional lensPosition; + /* 1/f so that brightness quadruples when this doubles */ + std::optional aperture; + /* proportional to brightness with 0 = no flash, 1 = maximum flash */ + std::optional flashIntensity; + /* Sensor reported temperature value (in degrees) */ + std::optional sensorTemperature; +}; diff --git a/src/ipa/rpi/controller/dpc_status.h b/src/ipa/rpi/controller/dpc_status.h new file mode 100644 index 00000000..46d0cf34 --- /dev/null +++ b/src/ipa/rpi/controller/dpc_status.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (C) 2019, Raspberry Pi Ltd + * + * dpc_status.h - DPC (defective pixel correction) control algorithm status + */ +#pragma once + +/* The "DPC" algorithm sets defective pixel correction strength. */ + +struct DpcStatus { + int strength; /* 0 = "off", 1 = "normal", 2 = "strong" */ +}; diff --git a/src/ipa/rpi/controller/geq_status.h b/src/ipa/rpi/controller/geq_status.h new file mode 100644 index 00000000..2d749fc9 --- /dev/null +++ b/src/ipa/rpi/controller/geq_status.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (C) 2019, Raspberry Pi Ltd + * + * geq_status.h - GEQ (green equalisation) control algorithm status + */ +#pragma once + +/* The "GEQ" algorithm calculates the green equalisation thresholds */ + +struct GeqStatus { + uint16_t offset; + double slope; +}; diff --git a/src/ipa/rpi/controller/histogram.cpp b/src/ipa/rpi/controller/histogram.cpp new file mode 100644 index 00000000..16a9207f --- /dev/null +++ b/src/ipa/rpi/controller/histogram.cpp @@ -0,0 +1,64 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (C) 2019, Raspberry Pi Ltd + * + * histogram.cpp - histogram calculations + */ +#include +#include + +#include "histogram.h" + +using namespace RPiController; + +uint64_t Histogram::cumulativeFreq(double bin) const +{ + if (bin <= 0) + return 0; + else if (bin >= bins()) + return total(); + int b = (int)bin; + return cumulative_[b] + + (bin - b) * (cumulative_[b + 1] - cumulative_[b]); +} + +double Histogram::quantile(double q, int first, int last) const +{ + if (first == -1) + first = 0; + if (last == -1) + last = cumulative_.size() - 2; + assert(first <= last); + uint64_t items = q * total(); + while (first < last) /* binary search to find the right bin */ + { + int middle = (first + last) / 2; + if (cumulative_[middle + 1] > items) + last = middle; /* between first and middle */ + else + first = middle + 1; /* after middle */ + } + assert(items >= cumulative_[first] && items <= cumulative_[last + 1]); + double frac = cumulative_[first + 1] == cumulative_[first] ? 0 + : (double)(items - cumulative_[first]) / + (cumulative_[first + 1] - cumulative_[first]); + return first + frac; +} + +double Histogram::interQuantileMean(double qLo, double qHi) const +{ + assert(qHi > qLo); + double pLo = quantile(qLo); + double pHi = quantile(qHi, (int)pLo); + double sumBinFreq = 0, cumulFreq = 0; + for (double pNext = floor(pLo) + 1.0; pNext <= ceil(pHi); + pLo = pNext, pNext += 1.0) { + int bin = floor(pLo); + double freq = (cumulative_[bin + 1] - cumulative_[bin]) * + (std::min(pNext, pHi) - pLo); + sumBinFreq += bin * freq; + cumulFreq += freq; + } + /* add 0.5 to give an average for bin mid-points */ + return sumBinFreq / cumulFreq + 0.5; +} diff --git a/src/ipa/rpi/controller/histogram.h b/src/ipa/rpi/controller/histogram.h new file mode 100644 index 00000000..6b3e3a9e --- /dev/null +++ b/src/ipa/rpi/controller/histogram.h @@ -0,0 +1,53 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (C) 2019, Raspberry Pi Ltd + * + * histogram.h - histogram calculation interface + */ +#pragma once + +#include +#include +#include + +/* + * A simple histogram class, for use in particular to find "quantiles" and + * averages between "quantiles". + */ + +namespace RPiController { + +class Histogram +{ +public: + Histogram() + { + cumulative_.push_back(0); + } + + template Histogram(T *histogram, int num) + { + assert(num); + cumulative_.reserve(num + 1); + cumulative_.push_back(0); + for (int i = 0; i < num; i++) + cumulative_.push_back(cumulative_.back() + + histogram[i]); + } + uint32_t bins() const { return cumulative_.size() - 1; } + uint64_t total() const { return cumulative_[cumulative_.size() - 1]; } + /* Cumulative frequency up to a (fractional) point in a bin. */ + uint64_t cumulativeFreq(double bin) const; + /* + * Return the (fractional) bin of the point q (0 <= q <= 1) through the + * histogram. Optionally provide limits to help. + */ + double quantile(double q, int first = -1, int last = -1) const; + /* Return the average histogram bin value between the two quantiles. */ + double interQuantileMean(double qLo, double qHi) const; + +private: + std::vector cumulative_; +}; + +} /* namespace RPiController */ diff --git a/src/ipa/rpi/controller/lux_status.h b/src/ipa/rpi/controller/lux_status.h new file mode 100644 index 00000000..5eb9faac --- /dev/null +++ b/src/ipa/rpi/controller/lux_status.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (C) 2019, Raspberry Pi Ltd + * + * lux_status.h - Lux control algorithm status + */ +#pragma once + +/* + * The "lux" algorithm looks at the (AGC) histogram statistics of the frame and + * estimates the current lux level of the scene. It does this by a simple ratio + * calculation comparing to a reference image that was taken in known conditions + * with known statistics and a properly measured lux level. There is a slight + * problem with aperture, in that it may be variable without the system knowing + * or being aware of it. In this case an external application may set a + * "current_aperture" value if it wishes, which would be used in place of the + * (presumably meaningless) value in the image metadata. + */ + +struct LuxStatus { + double lux; + double aperture; +}; diff --git a/src/ipa/rpi/controller/meson.build b/src/ipa/rpi/controller/meson.build new file mode 100644 index 00000000..feb0334e --- /dev/null +++ b/src/ipa/rpi/controller/meson.build @@ -0,0 +1,29 @@ +# SPDX-License-Identifier: CC0-1.0 + +rpi_ipa_controller_sources = files([ + 'algorithm.cpp', + 'controller.cpp', + 'device_status.cpp', + 'histogram.cpp', + 'pwl.cpp', + 'rpi/af.cpp', + 'rpi/agc.cpp', + 'rpi/alsc.cpp', + 'rpi/awb.cpp', + 'rpi/black_level.cpp', + 'rpi/ccm.cpp', + 'rpi/contrast.cpp', + 'rpi/dpc.cpp', + 'rpi/geq.cpp', + 'rpi/lux.cpp', + 'rpi/noise.cpp', + 'rpi/sdn.cpp', + 'rpi/sharpen.cpp', +]) + +rpi_ipa_controller_deps = [ + libcamera_private, +] + +rpi_ipa_controller_lib = static_library('rpi_ipa_controller', rpi_ipa_controller_sources, + dependencies : rpi_ipa_controller_deps) diff --git a/src/ipa/rpi/controller/metadata.h b/src/ipa/rpi/controller/metadata.h new file mode 100644 index 00000000..bf8a2393 --- /dev/null +++ b/src/ipa/rpi/controller/metadata.h @@ -0,0 +1,126 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (C) 2019-2021, Raspberry Pi Ltd + * + * metadata.h - general metadata class + */ +#pragma once + +/* A simple class for carrying arbitrary metadata, for example about an image. */ + +#include +#include +#include +#include + +#include + +namespace RPiController { + +class LIBCAMERA_TSA_CAPABILITY("mutex") Metadata +{ +public: + Metadata() = default; + + Metadata(Metadata const &other) + { + std::scoped_lock otherLock(other.mutex_); + data_ = other.data_; + } + + Metadata(Metadata &&other) + { + std::scoped_lock otherLock(other.mutex_); + data_ = std::move(other.data_); + other.data_.clear(); + } + + template + void set(std::string const &tag, T const &value) + { + std::scoped_lock lock(mutex_); + data_[tag] = value; + } + + template + int get(std::string const &tag, T &value) const + { + std::scoped_lock lock(mutex_); + auto it = data_.find(tag); + if (it == data_.end()) + return -1; + value = std::any_cast(it->second); + return 0; + } + + void clear() + { + std::scoped_lock lock(mutex_); + data_.clear(); + } + + Metadata &operator=(Metadata const &other) + { + std::scoped_lock lock(mutex_, other.mutex_); + data_ = other.data_; + return *this; + } + + Metadata &operator=(Metadata &&other) + { + std::scoped_lock lock(mutex_, other.mutex_); + data_ = std::move(other.data_); + other.data_.clear(); + return *this; + } + + void merge(Metadata &other) + { + std::scoped_lock lock(mutex_, other.mutex_); + data_.merge(other.data_); + } + + void mergeCopy(const Metadata &other) + { + std::scoped_lock lock(mutex_, other.mutex_); + /* + * If the metadata key exists, ignore this item and copy only + * unique key/value pairs. + */ + data_.insert(other.data_.begin(), other.data_.end()); + } + + template + T *getLocked(std::string const &tag) + { + /* + * This allows in-place access to the Metadata contents, + * for which you should be holding the lock. + */ + auto it = data_.find(tag); + if (it == data_.end()) + return nullptr; + return std::any_cast(&it->second); + } + + template + void setLocked(std::string const &tag, T const &value) + { + /* Use this only if you're holding the lock yourself. */ + data_[tag] = value; + } + + /* + * Note: use of (lowercase) lock and unlock means you can create scoped + * locks with the standard lock classes. + * e.g. std::lock_guard lock(metadata) + */ + void lock() LIBCAMERA_TSA_ACQUIRE() { mutex_.lock(); } + void unlock() LIBCAMERA_TSA_RELEASE() { mutex_.unlock(); } + +private: + mutable std::mutex mutex_; + std::map data_; +}; + +} /* namespace RPiController */ diff --git a/src/ipa/rpi/controller/noise_status.h b/src/ipa/rpi/controller/noise_status.h new file mode 100644 index 00000000..da194f71 --- /dev/null +++ b/src/ipa/rpi/controller/noise_status.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (C) 2019, Raspberry Pi Ltd + * + * noise_status.h - Noise control algorithm status + */ +#pragma once + +/* The "noise" algorithm stores an estimate of the noise profile for this image. */ + +struct NoiseStatus { + double noiseConstant; + double noiseSlope; +}; diff --git a/src/ipa/rpi/controller/pdaf_data.h b/src/ipa/rpi/controller/pdaf_data.h new file mode 100644 index 00000000..470510f2 --- /dev/null +++ b/src/ipa/rpi/controller/pdaf_data.h @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (C) 2022, Raspberry Pi Ltd + * + * pdaf_data.h - PDAF Metadata + */ +#pragma once + +#include + +#include "region_stats.h" + +namespace RPiController { + +struct PdafData { + /* Confidence, in arbitrary units */ + uint16_t conf; + /* Phase error, in s16 Q4 format (S.11.4) */ + int16_t phase; +}; + +using PdafRegions = RegionStats; + +} /* namespace RPiController */ diff --git a/src/ipa/rpi/controller/pwl.cpp b/src/ipa/rpi/controller/pwl.cpp new file mode 100644 index 00000000..70c2e24b --- /dev/null +++ b/src/ipa/rpi/controller/pwl.cpp @@ -0,0 +1,269 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (C) 2019, Raspberry Pi Ltd + * + * pwl.cpp - piecewise linear functions + */ + +#include +#include +#include + +#include "pwl.h" + +using namespace RPiController; + +int Pwl::read(const libcamera::YamlObject ¶ms) +{ + if (!params.size() || params.size() % 2) + return -EINVAL; + + const auto &list = params.asList(); + + for (auto it = list.begin(); it != list.end(); it++) { + auto x = it->get(); + if (!x) + return -EINVAL; + if (it != list.begin() && *x <= points_.back().x) + return -EINVAL; + + auto y = (++it)->get(); + if (!y) + return -EINVAL; + + points_.push_back(Point(*x, *y)); + } + + return 0; +} + +void Pwl::append(double x, double y, const double eps) +{ + if (points_.empty() || points_.back().x + eps < x) + points_.push_back(Point(x, y)); +} + +void Pwl::prepend(double x, double y, const double eps) +{ + if (points_.empty() || points_.front().x - eps > x) + points_.insert(points_.begin(), Point(x, y)); +} + +Pwl::Interval Pwl::domain() const +{ + return Interval(points_[0].x, points_[points_.size() - 1].x); +} + +Pwl::Interval Pwl::range() const +{ + double lo = points_[0].y, hi = lo; + for (auto &p : points_) + lo = std::min(lo, p.y), hi = std::max(hi, p.y); + return Interval(lo, hi); +} + +bool Pwl::empty() const +{ + return points_.empty(); +} + +double Pwl::eval(double x, int *spanPtr, bool updateSpan) const +{ + int span = findSpan(x, spanPtr && *spanPtr != -1 ? *spanPtr : points_.size() / 2 - 1); + if (spanPtr && updateSpan) + *spanPtr = span; + return points_[span].y + + (x - points_[span].x) * (points_[span + 1].y - points_[span].y) / + (points_[span + 1].x - points_[span].x); +} + +int Pwl::findSpan(double x, int span) const +{ + /* + * Pwls are generally small, so linear search may well be faster than + * binary, though could review this if large PWls start turning up. + */ + int lastSpan = points_.size() - 2; + /* + * some algorithms may call us with span pointing directly at the last + * control point + */ + span = std::max(0, std::min(lastSpan, span)); + while (span < lastSpan && x >= points_[span + 1].x) + span++; + while (span && x < points_[span].x) + span--; + return span; +} + +Pwl::PerpType Pwl::invert(Point const &xy, Point &perp, int &span, + const double eps) const +{ + assert(span >= -1); + bool prevOffEnd = false; + for (span = span + 1; span < (int)points_.size() - 1; span++) { + Point spanVec = points_[span + 1] - points_[span]; + double t = ((xy - points_[span]) % spanVec) / spanVec.len2(); + if (t < -eps) /* off the start of this span */ + { + if (span == 0) { + perp = points_[span]; + return PerpType::Start; + } else if (prevOffEnd) { + perp = points_[span]; + return PerpType::Vertex; + } + } else if (t > 1 + eps) /* off the end of this span */ + { + if (span == (int)points_.size() - 2) { + perp = points_[span + 1]; + return PerpType::End; + } + prevOffEnd = true; + } else /* a true perpendicular */ + { + perp = points_[span] + spanVec * t; + return PerpType::Perpendicular; + } + } + return PerpType::None; +} + +Pwl Pwl::inverse(bool *trueInverse, const double eps) const +{ + bool appended = false, prepended = false, neither = false; + Pwl inverse; + + for (Point const &p : points_) { + if (inverse.empty()) + inverse.append(p.y, p.x, eps); + else if (std::abs(inverse.points_.back().x - p.y) <= eps || + std::abs(inverse.points_.front().x - p.y) <= eps) + /* do nothing */; + else if (p.y > inverse.points_.back().x) { + inverse.append(p.y, p.x, eps); + appended = true; + } else if (p.y < inverse.points_.front().x) { + inverse.prepend(p.y, p.x, eps); + prepended = true; + } else + neither = true; + } + + /* + * This is not a proper inverse if we found ourselves putting points + * onto both ends of the inverse, or if there were points that couldn't + * go on either. + */ + if (trueInverse) + *trueInverse = !(neither || (appended && prepended)); + + return inverse; +} + +Pwl Pwl::compose(Pwl const &other, const double eps) const +{ + double thisX = points_[0].x, thisY = points_[0].y; + int thisSpan = 0, otherSpan = other.findSpan(thisY, 0); + Pwl result({ { thisX, other.eval(thisY, &otherSpan, false) } }); + while (thisSpan != (int)points_.size() - 1) { + double dx = points_[thisSpan + 1].x - points_[thisSpan].x, + dy = points_[thisSpan + 1].y - points_[thisSpan].y; + if (std::abs(dy) > eps && + otherSpan + 1 < (int)other.points_.size() && + points_[thisSpan + 1].y >= + other.points_[otherSpan + 1].x + eps) { + /* + * next control point in result will be where this + * function's y reaches the next span in other + */ + thisX = points_[thisSpan].x + + (other.points_[otherSpan + 1].x - + points_[thisSpan].y) * + dx / dy; + thisY = other.points_[++otherSpan].x; + } else if (std::abs(dy) > eps && otherSpan > 0 && + points_[thisSpan + 1].y <= + other.points_[otherSpan - 1].x - eps) { + /* + * next control point in result will be where this + * function's y reaches the previous span in other + */ + thisX = points_[thisSpan].x + + (other.points_[otherSpan + 1].x - + points_[thisSpan].y) * + dx / dy; + thisY = other.points_[--otherSpan].x; + } else { + /* we stay in the same span in other */ + thisSpan++; + thisX = points_[thisSpan].x, + thisY = points_[thisSpan].y; + } + result.append(thisX, other.eval(thisY, &otherSpan, false), + eps); + } + return result; +} + +void Pwl::map(std::function f) const +{ + for (auto &pt : points_) + f(pt.x, pt.y); +} + +void Pwl::map2(Pwl const &pwl0, Pwl const &pwl1, + std::function f) +{ + int span0 = 0, span1 = 0; + double x = std::min(pwl0.points_[0].x, pwl1.points_[0].x); + f(x, pwl0.eval(x, &span0, false), pwl1.eval(x, &span1, false)); + while (span0 < (int)pwl0.points_.size() - 1 || + span1 < (int)pwl1.points_.size() - 1) { + if (span0 == (int)pwl0.points_.size() - 1) + x = pwl1.points_[++span1].x; + else if (span1 == (int)pwl1.points_.size() - 1) + x = pwl0.points_[++span0].x; + else if (pwl0.points_[span0 + 1].x > pwl1.points_[span1 + 1].x) + x = pwl1.points_[++span1].x; + else + x = pwl0.points_[++span0].x; + f(x, pwl0.eval(x, &span0, false), pwl1.eval(x, &span1, false)); + } +} + +Pwl Pwl::combine(Pwl const &pwl0, Pwl const &pwl1, + std::function f, + const double eps) +{ + Pwl result; + map2(pwl0, pwl1, [&](double x, double y0, double y1) { + result.append(x, f(x, y0, y1), eps); + }); + return result; +} + +void Pwl::matchDomain(Interval const &domain, bool clip, const double eps) +{ + int span = 0; + prepend(domain.start, eval(clip ? points_[0].x : domain.start, &span), + eps); + span = points_.size() - 2; + append(domain.end, eval(clip ? points_.back().x : domain.end, &span), + eps); +} + +Pwl &Pwl::operator*=(double d) +{ + for (auto &pt : points_) + pt.y *= d; + return *this; +} + +void Pwl::debug(FILE *fp) const +{ + fprintf(fp, "Pwl {\n"); + for (auto &p : points_) + fprintf(fp, "\t(%g, %g)\n", p.x, p.y); + fprintf(fp, "}\n"); +} diff --git a/src/ipa/rpi/controller/pwl.h b/src/ipa/rpi/controller/pwl.h new file mode 100644 index 00000000..aacf6039 --- /dev/null +++ b/src/ipa/rpi/controller/pwl.h @@ -0,0 +1,127 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (C) 2019, Raspberry Pi Ltd + * + * pwl.h - piecewise linear functions interface + */ +#pragma once + +#include +#include +#include + +#include "libcamera/internal/yaml_parser.h" + +namespace RPiController { + +class Pwl +{ +public: + struct Interval { + Interval(double _start, double _end) + : start(_start), end(_end) + { + } + double start, end; + bool contains(double value) + { + return value >= start && value <= end; + } + double clip(double value) + { + return value < start ? start + : (value > end ? end : value); + } + double len() const { return end - start; } + }; + struct Point { + Point() : x(0), y(0) {} + Point(double _x, double _y) + : x(_x), y(_y) {} + double x, y; + Point operator-(Point const &p) const + { + return Point(x - p.x, y - p.y); + } + Point operator+(Point const &p) const + { + return Point(x + p.x, y + p.y); + } + double operator%(Point const &p) const + { + return x * p.x + y * p.y; + } + Point operator*(double f) const { return Point(x * f, y * f); } + Point operator/(double f) const { return Point(x / f, y / f); } + double len2() const { return x * x + y * y; } + double len() const { return sqrt(len2()); } + }; + Pwl() {} + Pwl(std::vector const &points) : points_(points) {} + int read(const libcamera::YamlObject ¶ms); + void append(double x, double y, const double eps = 1e-6); + void prepend(double x, double y, const double eps = 1e-6); + Interval domain() const; + Interval range() const; + bool empty() const; + /* + * Evaluate Pwl, optionally supplying an initial guess for the + * "span". The "span" may be optionally be updated. If you want to know + * the "span" value but don't have an initial guess you can set it to + * -1. + */ + double eval(double x, int *spanPtr = nullptr, + bool updateSpan = true) const; + /* + * Find perpendicular closest to xy, starting from span+1 so you can + * call it repeatedly to check for multiple closest points (set span to + * -1 on the first call). Also returns "pseudo" perpendiculars; see + * PerpType enum. + */ + enum class PerpType { + None, /* no perpendicular found */ + Start, /* start of Pwl is closest point */ + End, /* end of Pwl is closest point */ + Vertex, /* vertex of Pwl is closest point */ + Perpendicular /* true perpendicular found */ + }; + PerpType invert(Point const &xy, Point &perp, int &span, + const double eps = 1e-6) const; + /* + * Compute the inverse function. Indicate if it is a proper (true) + * inverse, or only a best effort (e.g. input was non-monotonic). + */ + Pwl inverse(bool *trueInverse = nullptr, const double eps = 1e-6) const; + /* Compose two Pwls together, doing "this" first and "other" after. */ + Pwl compose(Pwl const &other, const double eps = 1e-6) const; + /* Apply function to (x,y) values at every control point. */ + void map(std::function f) const; + /* + * Apply function to (x, y0, y1) values wherever either Pwl has a + * control point. + */ + static void map2(Pwl const &pwl0, Pwl const &pwl1, + std::function f); + /* + * Combine two Pwls, meaning we create a new Pwl where the y values are + * given by running f wherever either has a knot. + */ + static Pwl + combine(Pwl const &pwl0, Pwl const &pwl1, + std::function f, + const double eps = 1e-6); + /* + * Make "this" match (at least) the given domain. Any extension my be + * clipped or linear. + */ + void matchDomain(Interval const &domain, bool clip = true, + const double eps = 1e-6); + Pwl &operator*=(double d); + void debug(FILE *fp = stdout) const; + +private: + int findSpan(double x, int span) const; + std::vector points_; +}; + +} /* namespace RPiController */ diff --git a/src/ipa/rpi/controller/region_stats.h b/src/ipa/rpi/controller/region_stats.h new file mode 100644 index 00000000..a8860dc8 --- /dev/null +++ b/src/ipa/rpi/controller/region_stats.h @@ -0,0 +1,123 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (C) 2022, Raspberry Pi Ltd + * + * region_stats.h - Raspberry Pi region based statistics container + */ +#pragma once + +#include +#include +#include + +#include + +namespace RPiController { + +template +class RegionStats +{ +public: + struct Region { + T val; + uint32_t counted; + uint32_t uncounted; + }; + + RegionStats() + : size_({}), numFloating_(0), default_({}) + { + } + + void init(const libcamera::Size &size, unsigned int numFloating = 0) + { + size_ = size; + numFloating_ = numFloating; + regions_.clear(); + regions_.resize(size_.width * size_.height + numFloating_); + } + + void init(unsigned int num) + { + size_ = libcamera::Size(num, 1); + numFloating_ = 0; + regions_.clear(); + regions_.resize(num); + } + + unsigned int numRegions() const + { + return size_.width * size_.height; + } + + unsigned int numFloatingRegions() const + { + return numFloating_; + } + + libcamera::Size size() const + { + return size_; + } + + void set(unsigned int index, const Region ®ion) + { + if (index >= numRegions()) + return; + set_(index, region); + } + + void set(const libcamera::Point &pos, const Region ®ion) + { + set(pos.y * size_.width + pos.x, region); + } + + void setFloating(unsigned int index, const Region ®ion) + { + if (index >= numFloatingRegions()) + return; + set(numRegions() + index, region); + } + + const Region &get(unsigned int index) const + { + if (index >= numRegions()) + return default_; + return get_(index); + } + + const Region &get(const libcamera::Point &pos) const + { + return get(pos.y * size_.width + pos.x); + } + + const Region &getFloating(unsigned int index) const + { + if (index >= numFloatingRegions()) + return default_; + return get_(numRegions() + index); + } + + typename std::vector::iterator begin() { return regions_.begin(); } + typename std::vector::iterator end() { return regions_.end(); } + typename std::vector::const_iterator begin() const { return regions_.begin(); } + typename std::vector::const_iterator end() const { return regions_.end(); } + +private: + void set_(unsigned int index, const Region ®ion) + { + regions_[index] = region; + } + + const Region &get_(unsigned int index) const + { + return regions_[index]; + } + + libcamera::Size size_; + unsigned int numFloating_; + std::vector regions_; + Region default_; +}; + +} /* namespace RPiController */ diff --git a/src/ipa/rpi/controller/rpi/af.cpp b/src/ipa/rpi/controller/rpi/af.cpp new file mode 100644 index 00000000..ed0c8a94 --- /dev/null +++ b/src/ipa/rpi/controller/rpi/af.cpp @@ -0,0 +1,797 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (C) 2022-2023, Raspberry Pi Ltd + * + * af.cpp - Autofocus control algorithm + */ + +#include "af.h" + +#include +#include +#include + +#include + +#include + +using namespace RPiController; +using namespace libcamera; + +LOG_DEFINE_CATEGORY(RPiAf) + +#define NAME "rpi.af" + +/* + * Default values for parameters. All may be overridden in the tuning file. + * Many of these values are sensor- or module-dependent; the defaults here + * assume IMX708 in a Raspberry Pi V3 camera with the standard lens. + * + * Here all focus values are in dioptres (1/m). They are converted to hardware + * units when written to status.lensSetting or returned from setLensPosition(). + * + * Gain and delay values are relative to the update rate, since much (not all) + * of the delay is in the sensor and (for CDAF) ISP, not the lens mechanism; + * but note that algorithms are updated at no more than 30 Hz. + */ + +Af::RangeDependentParams::RangeDependentParams() + : focusMin(0.0), + focusMax(12.0), + focusDefault(1.0) +{ +} + +Af::SpeedDependentParams::SpeedDependentParams() + : stepCoarse(1.0), + stepFine(0.25), + contrastRatio(0.75), + pdafGain(-0.02), + pdafSquelch(0.125), + maxSlew(2.0), + pdafFrames(20), + dropoutFrames(6), + stepFrames(4) +{ +} + +Af::CfgParams::CfgParams() + : confEpsilon(8), + confThresh(16), + confClip(512), + skipFrames(5), + map() +{ +} + +template +static void readNumber(T &dest, const libcamera::YamlObject ¶ms, char const *name) +{ + auto value = params[name].get(); + if (value) + dest = *value; + else + LOG(RPiAf, Warning) << "Missing parameter \"" << name << "\""; +} + +void Af::RangeDependentParams::read(const libcamera::YamlObject ¶ms) +{ + + readNumber(focusMin, params, "min"); + readNumber(focusMax, params, "max"); + readNumber(focusDefault, params, "default"); +} + +void Af::SpeedDependentParams::read(const libcamera::YamlObject ¶ms) +{ + readNumber(stepCoarse, params, "step_coarse"); + readNumber(stepFine, params, "step_fine"); + readNumber(contrastRatio, params, "contrast_ratio"); + readNumber(pdafGain, params, "pdaf_gain"); + readNumber(pdafSquelch, params, "pdaf_squelch"); + readNumber(maxSlew, params, "max_slew"); + readNumber(pdafFrames, params, "pdaf_frames"); + readNumber(dropoutFrames, params, "dropout_frames"); + readNumber(stepFrames, params, "step_frames"); +} + +int Af::CfgParams::read(const libcamera::YamlObject ¶ms) +{ + if (params.contains("ranges")) { + auto &rr = params["ranges"]; + + if (rr.contains("normal")) + ranges[AfRangeNormal].read(rr["normal"]); + else + LOG(RPiAf, Warning) << "Missing range \"normal\""; + + ranges[AfRangeMacro] = ranges[AfRangeNormal]; + if (rr.contains("macro")) + ranges[AfRangeMacro].read(rr["macro"]); + + ranges[AfRangeFull].focusMin = std::min(ranges[AfRangeNormal].focusMin, + ranges[AfRangeMacro].focusMin); + ranges[AfRangeFull].focusMax = std::max(ranges[AfRangeNormal].focusMax, + ranges[AfRangeMacro].focusMax); + ranges[AfRangeFull].focusDefault = ranges[AfRangeNormal].focusDefault; + if (rr.contains("full")) + ranges[AfRangeFull].read(rr["full"]); + } else + LOG(RPiAf, Warning) << "No ranges defined"; + + if (params.contains("speeds")) { + auto &ss = params["speeds"]; + + if (ss.contains("normal")) + speeds[AfSpeedNormal].read(ss["normal"]); + else + LOG(RPiAf, Warning) << "Missing speed \"normal\""; + + speeds[AfSpeedFast] = speeds[AfSpeedNormal]; + if (ss.contains("fast")) + speeds[AfSpeedFast].read(ss["fast"]); + } else + LOG(RPiAf, Warning) << "No speeds defined"; + + readNumber(confEpsilon, params, "conf_epsilon"); + readNumber(confThresh, params, "conf_thresh"); + readNumber(confClip, params, "conf_clip"); + readNumber(skipFrames, params, "skip_frames"); + + if (params.contains("map")) + map.read(params["map"]); + else + LOG(RPiAf, Warning) << "No map defined"; + + return 0; +} + +void Af::CfgParams::initialise() +{ + if (map.empty()) { + /* Default mapping from dioptres to hardware setting */ + static constexpr double DefaultMapX0 = 0.0; + static constexpr double DefaultMapY0 = 445.0; + static constexpr double DefaultMapX1 = 15.0; + static constexpr double DefaultMapY1 = 925.0; + + map.append(DefaultMapX0, DefaultMapY0); + map.append(DefaultMapX1, DefaultMapY1); + } +} + +/* Af Algorithm class */ + +static constexpr unsigned MaxWindows = 10; + +Af::Af(Controller *controller) + : AfAlgorithm(controller), + cfg_(), + range_(AfRangeNormal), + speed_(AfSpeedNormal), + mode_(AfAlgorithm::AfModeManual), + pauseFlag_(false), + statsRegion_(0, 0, 0, 0), + windows_(), + useWindows_(false), + phaseWeights_(), + contrastWeights_(), + scanState_(ScanState::Idle), + initted_(false), + ftarget_(-1.0), + fsmooth_(-1.0), + prevContrast_(0.0), + skipCount_(0), + stepCount_(0), + dropCount_(0), + scanMaxContrast_(0.0), + scanMinContrast_(1.0e9), + scanData_(), + reportState_(AfState::Idle) +{ + /* + * Reserve space for data, to reduce memory fragmentation. It's too early + * to query the size of the PDAF (from camera) and Contrast (from ISP) + * statistics, but these are plausible upper bounds. + */ + phaseWeights_.w.reserve(16 * 12); + contrastWeights_.w.reserve(getHardwareConfig().focusRegions.width * + getHardwareConfig().focusRegions.height); + scanData_.reserve(32); +} + +Af::~Af() +{ +} + +char const *Af::name() const +{ + return NAME; +} + +int Af::read(const libcamera::YamlObject ¶ms) +{ + return cfg_.read(params); +} + +void Af::initialise() +{ + cfg_.initialise(); +} + +void Af::switchMode(CameraMode const &cameraMode, [[maybe_unused]] Metadata *metadata) +{ + (void)metadata; + + /* Assume that PDAF and Focus stats grids cover the visible area */ + statsRegion_.x = (int)cameraMode.cropX; + statsRegion_.y = (int)cameraMode.cropY; + statsRegion_.width = (unsigned)(cameraMode.width * cameraMode.scaleX); + statsRegion_.height = (unsigned)(cameraMode.height * cameraMode.scaleY); + LOG(RPiAf, Debug) << "switchMode: statsRegion: " + << statsRegion_.x << ',' + << statsRegion_.y << ',' + << statsRegion_.width << ',' + << statsRegion_.height; + invalidateWeights(); + + if (scanState_ >= ScanState::Coarse && scanState_ < ScanState::Settle) { + /* + * If a scan was in progress, re-start it, as CDAF statistics + * may have changed. Though if the application is just about + * to take a still picture, this will not help... + */ + startProgrammedScan(); + } + skipCount_ = cfg_.skipFrames; +} + +void Af::computeWeights(RegionWeights *wgts, unsigned rows, unsigned cols) +{ + wgts->rows = rows; + wgts->cols = cols; + wgts->sum = 0; + wgts->w.resize(rows * cols); + std::fill(wgts->w.begin(), wgts->w.end(), 0); + + if (rows > 0 && cols > 0 && useWindows_ && + statsRegion_.height >= rows && statsRegion_.width >= cols) { + /* + * Here we just merge all of the given windows, weighted by area. + * \todo Perhaps a better approach might be to find the phase in each + * window and choose either the closest or the highest-confidence one? + * Ensure weights sum to less than (1<<16). 46080 is a "round number" + * below 65536, for better rounding when window size is a simple + * fraction of image dimensions. + */ + const unsigned maxCellWeight = 46080u / (MaxWindows * rows * cols); + const unsigned cellH = statsRegion_.height / rows; + const unsigned cellW = statsRegion_.width / cols; + const unsigned cellA = cellH * cellW; + + for (auto &w : windows_) { + for (unsigned r = 0; r < rows; ++r) { + int y0 = std::max(statsRegion_.y + (int)(cellH * r), w.y); + int y1 = std::min(statsRegion_.y + (int)(cellH * (r + 1)), + w.y + (int)(w.height)); + if (y0 >= y1) + continue; + y1 -= y0; + for (unsigned c = 0; c < cols; ++c) { + int x0 = std::max(statsRegion_.x + (int)(cellW * c), w.x); + int x1 = std::min(statsRegion_.x + (int)(cellW * (c + 1)), + w.x + (int)(w.width)); + if (x0 >= x1) + continue; + unsigned a = y1 * (x1 - x0); + a = (maxCellWeight * a + cellA - 1) / cellA; + wgts->w[r * cols + c] += a; + wgts->sum += a; + } + } + } + } + + if (wgts->sum == 0) { + /* Default AF window is the middle 1/2 width of the middle 1/3 height */ + for (unsigned r = rows / 3; r < rows - rows / 3; ++r) { + for (unsigned c = cols / 4; c < cols - cols / 4; ++c) { + wgts->w[r * cols + c] = 1; + wgts->sum += 1; + } + } + } +} + +void Af::invalidateWeights() +{ + phaseWeights_.sum = 0; + contrastWeights_.sum = 0; +} + +bool Af::getPhase(PdafRegions const ®ions, double &phase, double &conf) +{ + libcamera::Size size = regions.size(); + if (size.height != phaseWeights_.rows || size.width != phaseWeights_.cols || + phaseWeights_.sum == 0) { + LOG(RPiAf, Debug) << "Recompute Phase weights " << size.width << 'x' << size.height; + computeWeights(&phaseWeights_, size.height, size.width); + } + + uint32_t sumWc = 0; + int64_t sumWcp = 0; + for (unsigned i = 0; i < regions.numRegions(); ++i) { + unsigned w = phaseWeights_.w[i]; + if (w) { + const PdafData &data = regions.get(i).val; + unsigned c = data.conf; + if (c >= cfg_.confThresh) { + if (c > cfg_.confClip) + c = cfg_.confClip; + c -= (cfg_.confThresh >> 2); + sumWc += w * c; + c -= (cfg_.confThresh >> 2); + sumWcp += (int64_t)(w * c) * (int64_t)data.phase; + } + } + } + + if (0 < phaseWeights_.sum && phaseWeights_.sum <= sumWc) { + phase = (double)sumWcp / (double)sumWc; + conf = (double)sumWc / (double)phaseWeights_.sum; + return true; + } else { + phase = 0.0; + conf = 0.0; + return false; + } +} + +double Af::getContrast(const FocusRegions &focusStats) +{ + libcamera::Size size = focusStats.size(); + if (size.height != contrastWeights_.rows || + size.width != contrastWeights_.cols || contrastWeights_.sum == 0) { + LOG(RPiAf, Debug) << "Recompute Contrast weights " + << size.width << 'x' << size.height; + computeWeights(&contrastWeights_, size.height, size.width); + } + + uint64_t sumWc = 0; + for (unsigned i = 0; i < focusStats.numRegions(); ++i) + sumWc += contrastWeights_.w[i] * focusStats.get(i).val; + + return (contrastWeights_.sum > 0) ? ((double)sumWc / (double)contrastWeights_.sum) : 0.0; +} + +void Af::doPDAF(double phase, double conf) +{ + /* Apply loop gain */ + phase *= cfg_.speeds[speed_].pdafGain; + + if (mode_ == AfModeContinuous) { + /* + * PDAF in Continuous mode. Scale down lens movement when + * delta is small or confidence is low, to suppress wobble. + */ + phase *= conf / (conf + cfg_.confEpsilon); + if (std::abs(phase) < cfg_.speeds[speed_].pdafSquelch) { + double a = phase / cfg_.speeds[speed_].pdafSquelch; + phase *= a * a; + } + } else { + /* + * PDAF in triggered-auto mode. Allow early termination when + * phase delta is small; scale down lens movements towards + * the end of the sequence, to ensure a stable image. + */ + if (stepCount_ >= cfg_.speeds[speed_].stepFrames) { + if (std::abs(phase) < cfg_.speeds[speed_].pdafSquelch) + stepCount_ = cfg_.speeds[speed_].stepFrames; + } else + phase *= stepCount_ / cfg_.speeds[speed_].stepFrames; + } + + /* Apply slew rate limit. Report failure if out of bounds. */ + if (phase < -cfg_.speeds[speed_].maxSlew) { + phase = -cfg_.speeds[speed_].maxSlew; + reportState_ = (ftarget_ <= cfg_.ranges[range_].focusMin) ? AfState::Failed + : AfState::Scanning; + } else if (phase > cfg_.speeds[speed_].maxSlew) { + phase = cfg_.speeds[speed_].maxSlew; + reportState_ = (ftarget_ >= cfg_.ranges[range_].focusMax) ? AfState::Failed + : AfState::Scanning; + } else + reportState_ = AfState::Focused; + + ftarget_ = fsmooth_ + phase; +} + +bool Af::earlyTerminationByPhase(double phase) +{ + if (scanData_.size() > 0 && + scanData_[scanData_.size() - 1].conf >= cfg_.confEpsilon) { + double oldFocus = scanData_[scanData_.size() - 1].focus; + double oldPhase = scanData_[scanData_.size() - 1].phase; + + /* + * Check that the gradient is finite and has the expected sign; + * Interpolate/extrapolate the lens position for zero phase. + * Check that the extrapolation is well-conditioned. + */ + if ((ftarget_ - oldFocus) * (phase - oldPhase) > 0.0) { + double param = phase / (phase - oldPhase); + if (-3.0 <= param && param <= 3.5) { + ftarget_ += param * (oldFocus - ftarget_); + LOG(RPiAf, Debug) << "ETBP: param=" << param; + return true; + } + } + } + + return false; +} + +double Af::findPeak(unsigned i) const +{ + double f = scanData_[i].focus; + + if (i > 0 && i + 1 < scanData_.size()) { + double dropLo = scanData_[i].contrast - scanData_[i - 1].contrast; + double dropHi = scanData_[i].contrast - scanData_[i + 1].contrast; + if (0.0 <= dropLo && dropLo < dropHi) { + double param = 0.3125 * (1.0 - dropLo / dropHi) * (1.6 - dropLo / dropHi); + f += param * (scanData_[i - 1].focus - f); + } else if (0.0 <= dropHi && dropHi < dropLo) { + double param = 0.3125 * (1.0 - dropHi / dropLo) * (1.6 - dropHi / dropLo); + f += param * (scanData_[i + 1].focus - f); + } + } + + LOG(RPiAf, Debug) << "FindPeak: " << f; + return f; +} + +void Af::doScan(double contrast, double phase, double conf) +{ + /* Record lens position, contrast and phase values for the current scan */ + if (scanData_.empty() || contrast > scanMaxContrast_) { + scanMaxContrast_ = contrast; + scanMaxIndex_ = scanData_.size(); + } + if (contrast < scanMinContrast_) + scanMinContrast_ = contrast; + scanData_.emplace_back(ScanRecord{ ftarget_, contrast, phase, conf }); + + if (scanState_ == ScanState::Coarse) { + if (ftarget_ >= cfg_.ranges[range_].focusMax || + contrast < cfg_.speeds[speed_].contrastRatio * scanMaxContrast_) { + /* + * Finished course scan, or termination based on contrast. + * Jump to just after max contrast and start fine scan. + */ + ftarget_ = std::min(ftarget_, findPeak(scanMaxIndex_) + + 2.0 * cfg_.speeds[speed_].stepFine); + scanState_ = ScanState::Fine; + scanData_.clear(); + } else + ftarget_ += cfg_.speeds[speed_].stepCoarse; + } else { /* ScanState::Fine */ + if (ftarget_ <= cfg_.ranges[range_].focusMin || scanData_.size() >= 5 || + contrast < cfg_.speeds[speed_].contrastRatio * scanMaxContrast_) { + /* + * Finished fine scan, or termination based on contrast. + * Use quadratic peak-finding to find best contrast position. + */ + ftarget_ = findPeak(scanMaxIndex_); + scanState_ = ScanState::Settle; + } else + ftarget_ -= cfg_.speeds[speed_].stepFine; + } + + stepCount_ = (ftarget_ == fsmooth_) ? 0 : cfg_.speeds[speed_].stepFrames; +} + +void Af::doAF(double contrast, double phase, double conf) +{ + /* Skip frames at startup and after sensor mode change */ + if (skipCount_ > 0) { + LOG(RPiAf, Debug) << "SKIP"; + skipCount_--; + return; + } + + if (scanState_ == ScanState::Pdaf) { + /* + * Use PDAF closed-loop control whenever available, in both CAF + * mode and (for a limited number of iterations) when triggered. + * If PDAF fails (due to poor contrast, noise or large defocus), + * fall back to a CDAF-based scan. To avoid "nuisance" scans, + * scan only after a number of frames with low PDAF confidence. + */ + if (conf > (dropCount_ ? 1.0 : 0.25) * cfg_.confEpsilon) { + doPDAF(phase, conf); + if (stepCount_ > 0) + stepCount_--; + else if (mode_ != AfModeContinuous) + scanState_ = ScanState::Idle; + dropCount_ = 0; + } else if (++dropCount_ == cfg_.speeds[speed_].dropoutFrames) + startProgrammedScan(); + } else if (scanState_ >= ScanState::Coarse && fsmooth_ == ftarget_) { + /* + * Scanning sequence. This means PDAF has become unavailable. + * Allow a delay between steps for CDAF FoM statistics to be + * updated, and a "settling time" at the end of the sequence. + * [A coarse or fine scan can be abandoned if two PDAF samples + * allow direct interpolation of the zero-phase lens position.] + */ + if (stepCount_ > 0) + stepCount_--; + else if (scanState_ == ScanState::Settle) { + if (prevContrast_ >= cfg_.speeds[speed_].contrastRatio * scanMaxContrast_ && + scanMinContrast_ <= cfg_.speeds[speed_].contrastRatio * scanMaxContrast_) + reportState_ = AfState::Focused; + else + reportState_ = AfState::Failed; + if (mode_ == AfModeContinuous && !pauseFlag_ && + cfg_.speeds[speed_].dropoutFrames > 0) + scanState_ = ScanState::Pdaf; + else + scanState_ = ScanState::Idle; + scanData_.clear(); + } else if (conf >= cfg_.confEpsilon && earlyTerminationByPhase(phase)) { + scanState_ = ScanState::Settle; + stepCount_ = (mode_ == AfModeContinuous) ? 0 + : cfg_.speeds[speed_].stepFrames; + } else + doScan(contrast, phase, conf); + } +} + +void Af::updateLensPosition() +{ + if (scanState_ >= ScanState::Pdaf) { + ftarget_ = std::clamp(ftarget_, + cfg_.ranges[range_].focusMin, + cfg_.ranges[range_].focusMax); + } + + if (initted_) { + /* from a known lens position: apply slew rate limit */ + fsmooth_ = std::clamp(ftarget_, + fsmooth_ - cfg_.speeds[speed_].maxSlew, + fsmooth_ + cfg_.speeds[speed_].maxSlew); + } else { + /* from an unknown position: go straight to target, but add delay */ + fsmooth_ = ftarget_; + initted_ = true; + skipCount_ = cfg_.skipFrames; + } +} + +void Af::startAF() +{ + /* Use PDAF if the tuning file allows it; else CDAF. */ + if (cfg_.speeds[speed_].dropoutFrames > 0 && + (mode_ == AfModeContinuous || cfg_.speeds[speed_].pdafFrames > 0)) { + if (!initted_) { + ftarget_ = cfg_.ranges[range_].focusDefault; + updateLensPosition(); + } + stepCount_ = (mode_ == AfModeContinuous) ? 0 : cfg_.speeds[speed_].pdafFrames; + scanState_ = ScanState::Pdaf; + scanData_.clear(); + dropCount_ = 0; + reportState_ = AfState::Scanning; + } else + startProgrammedScan(); +} + +void Af::startProgrammedScan() +{ + ftarget_ = cfg_.ranges[range_].focusMin; + updateLensPosition(); + scanState_ = ScanState::Coarse; + scanMaxContrast_ = 0.0; + scanMinContrast_ = 1.0e9; + scanMaxIndex_ = 0; + scanData_.clear(); + stepCount_ = cfg_.speeds[speed_].stepFrames; + reportState_ = AfState::Scanning; +} + +void Af::goIdle() +{ + scanState_ = ScanState::Idle; + reportState_ = AfState::Idle; + scanData_.clear(); +} + +/* + * PDAF phase data are available in prepare(), but CDAF statistics are not + * available until process(). We are gambling on the availability of PDAF. + * To expedite feedback control using PDAF, issue the V4L2 lens control from + * prepare(). Conversely, during scans, we must allow an extra frame delay + * between steps, to retrieve CDAF statistics from the previous process() + * so we can terminate the scan early without having to change our minds. + */ + +void Af::prepare(Metadata *imageMetadata) +{ + /* Initialize for triggered scan or start of CAF mode */ + if (scanState_ == ScanState::Trigger) + startAF(); + + if (initted_) { + /* Get PDAF from the embedded metadata, and run AF algorithm core */ + PdafRegions regions; + double phase = 0.0, conf = 0.0; + double oldFt = ftarget_; + double oldFs = fsmooth_; + ScanState oldSs = scanState_; + uint32_t oldSt = stepCount_; + if (imageMetadata->get("pdaf.regions", regions) == 0) + getPhase(regions, phase, conf); + doAF(prevContrast_, phase, conf); + updateLensPosition(); + LOG(RPiAf, Debug) << std::fixed << std::setprecision(2) + << static_cast(reportState_) + << " sst" << static_cast(oldSs) + << "->" << static_cast(scanState_) + << " stp" << oldSt << "->" << stepCount_ + << " ft" << oldFt << "->" << ftarget_ + << " fs" << oldFs << "->" << fsmooth_ + << " cont=" << (int)prevContrast_ + << " phase=" << (int)phase << " conf=" << (int)conf; + } + + /* Report status and produce new lens setting */ + AfStatus status; + if (pauseFlag_) + status.pauseState = (scanState_ == ScanState::Idle) ? AfPauseState::Paused + : AfPauseState::Pausing; + else + status.pauseState = AfPauseState::Running; + + if (mode_ == AfModeAuto && scanState_ != ScanState::Idle) + status.state = AfState::Scanning; + else + status.state = reportState_; + status.lensSetting = initted_ ? std::optional(cfg_.map.eval(fsmooth_)) + : std::nullopt; + imageMetadata->set("af.status", status); +} + +void Af::process(StatisticsPtr &stats, [[maybe_unused]] Metadata *imageMetadata) +{ + (void)imageMetadata; + prevContrast_ = getContrast(stats->focusRegions); +} + +/* Controls */ + +void Af::setRange(AfRange r) +{ + LOG(RPiAf, Debug) << "setRange: " << (unsigned)r; + if (r < AfAlgorithm::AfRangeMax) + range_ = r; +} + +void Af::setSpeed(AfSpeed s) +{ + LOG(RPiAf, Debug) << "setSpeed: " << (unsigned)s; + if (s < AfAlgorithm::AfSpeedMax) { + if (scanState_ == ScanState::Pdaf && + cfg_.speeds[s].pdafFrames > cfg_.speeds[speed_].pdafFrames) + stepCount_ += cfg_.speeds[s].pdafFrames - cfg_.speeds[speed_].pdafFrames; + speed_ = s; + } +} + +void Af::setMetering(bool mode) +{ + if (useWindows_ != mode) { + useWindows_ = mode; + invalidateWeights(); + } +} + +void Af::setWindows(libcamera::Span const &wins) +{ + windows_.clear(); + for (auto &w : wins) { + LOG(RPiAf, Debug) << "Window: " + << w.x << ", " + << w.y << ", " + << w.width << ", " + << w.height; + windows_.push_back(w); + if (windows_.size() >= MaxWindows) + break; + } + + if (useWindows_) + invalidateWeights(); +} + +bool Af::setLensPosition(double dioptres, int *hwpos) +{ + bool changed = false; + + if (mode_ == AfModeManual) { + LOG(RPiAf, Debug) << "setLensPosition: " << dioptres; + ftarget_ = cfg_.map.domain().clip(dioptres); + changed = !(initted_ && fsmooth_ == ftarget_); + updateLensPosition(); + } + + if (hwpos) + *hwpos = cfg_.map.eval(fsmooth_); + + return changed; +} + +std::optional Af::getLensPosition() const +{ + /* + * \todo We ought to perform some precise timing here to determine + * the current lens position. + */ + return initted_ ? std::optional(fsmooth_) : std::nullopt; +} + +void Af::cancelScan() +{ + LOG(RPiAf, Debug) << "cancelScan"; + if (mode_ == AfModeAuto) + goIdle(); +} + +void Af::triggerScan() +{ + LOG(RPiAf, Debug) << "triggerScan"; + if (mode_ == AfModeAuto && scanState_ == ScanState::Idle) + scanState_ = ScanState::Trigger; +} + +void Af::setMode(AfAlgorithm::AfMode mode) +{ + LOG(RPiAf, Debug) << "setMode: " << (unsigned)mode; + if (mode_ != mode) { + mode_ = mode; + pauseFlag_ = false; + if (mode == AfModeContinuous) + scanState_ = ScanState::Trigger; + else if (mode != AfModeAuto || scanState_ < ScanState::Coarse) + goIdle(); + } +} + +AfAlgorithm::AfMode Af::getMode() const +{ + return mode_; +} + +void Af::pause(AfAlgorithm::AfPause pause) +{ + LOG(RPiAf, Debug) << "pause: " << (unsigned)pause; + if (mode_ == AfModeContinuous) { + if (pause == AfPauseResume && pauseFlag_) { + pauseFlag_ = false; + if (scanState_ < ScanState::Coarse) + scanState_ = ScanState::Trigger; + } else if (pause != AfPauseResume && !pauseFlag_) { + pauseFlag_ = true; + if (pause == AfPauseImmediate || scanState_ < ScanState::Coarse) + goIdle(); + } + } +} + +// Register algorithm with the system. +static Algorithm *create(Controller *controller) +{ + return (Algorithm *)new Af(controller); +} +static RegisterAlgorithm reg(NAME, &create); diff --git a/src/ipa/rpi/controller/rpi/af.h b/src/ipa/rpi/controller/rpi/af.h new file mode 100644 index 00000000..6d2bae67 --- /dev/null +++ b/src/ipa/rpi/controller/rpi/af.h @@ -0,0 +1,165 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (C) 2022-2023, Raspberry Pi Ltd + * + * af.h - Autofocus control algorithm + */ +#pragma once + +#include "../af_algorithm.h" +#include "../af_status.h" +#include "../pdaf_data.h" +#include "../pwl.h" + +/* + * This algorithm implements a hybrid of CDAF and PDAF, favouring PDAF. + * + * Whenever PDAF is available, it is used in a continuous feedback loop. + * When triggered in auto mode, we simply enable AF for a limited number + * of frames (it may terminate early if the delta becomes small enough). + * + * When PDAF confidence is low (due e.g. to low contrast or extreme defocus) + * or PDAF data are absent, fall back to CDAF with a programmed scan pattern. + * A coarse and fine scan are performed, using ISP's CDAF focus FoM to + * estimate the lens position with peak contrast. This is slower due to + * extra latency in the ISP, and requires a settling time between steps. + * + * Some hysteresis is applied to the switch between PDAF and CDAF, to avoid + * "nuisance" scans. During each interval where PDAF is not working, only + * ONE scan will be performed; CAF cannot track objects using CDAF alone. + * + */ + +namespace RPiController { + +class Af : public AfAlgorithm +{ +public: + Af(Controller *controller = NULL); + ~Af(); + char const *name() const override; + int read(const libcamera::YamlObject ¶ms) override; + void initialise() override; + + /* IPA calls */ + void switchMode(CameraMode const &cameraMode, Metadata *metadata) override; + void prepare(Metadata *imageMetadata) override; + void process(StatisticsPtr &stats, Metadata *imageMetadata) override; + + /* controls */ + void setRange(AfRange range) override; + void setSpeed(AfSpeed speed) override; + void setMetering(bool use_windows) override; + void setWindows(libcamera::Span const &wins) override; + void setMode(AfMode mode) override; + AfMode getMode() const override; + bool setLensPosition(double dioptres, int32_t *hwpos) override; + std::optional getLensPosition() const override; + void triggerScan() override; + void cancelScan() override; + void pause(AfPause pause) override; + +private: + enum class ScanState { + Idle = 0, + Trigger, + Pdaf, + Coarse, + Fine, + Settle + }; + + struct RangeDependentParams { + double focusMin; /* lower (far) limit in dipotres */ + double focusMax; /* upper (near) limit in dioptres */ + double focusDefault; /* default setting ("hyperfocal") */ + + RangeDependentParams(); + void read(const libcamera::YamlObject ¶ms); + }; + + struct SpeedDependentParams { + double stepCoarse; /* used for scans */ + double stepFine; /* used for scans */ + double contrastRatio; /* used for scan termination and reporting */ + double pdafGain; /* coefficient for PDAF feedback loop */ + double pdafSquelch; /* PDAF stability parameter (device-specific) */ + double maxSlew; /* limit for lens movement per frame */ + uint32_t pdafFrames; /* number of iterations when triggered */ + uint32_t dropoutFrames; /* number of non-PDAF frames to switch to CDAF */ + uint32_t stepFrames; /* frames to skip in between steps of a scan */ + + SpeedDependentParams(); + void read(const libcamera::YamlObject ¶ms); + }; + + struct CfgParams { + RangeDependentParams ranges[AfRangeMax]; + SpeedDependentParams speeds[AfSpeedMax]; + uint32_t confEpsilon; /* PDAF hysteresis threshold (sensor-specific) */ + uint32_t confThresh; /* PDAF confidence cell min (sensor-specific) */ + uint32_t confClip; /* PDAF confidence cell max (sensor-specific) */ + uint32_t skipFrames; /* frames to skip at start or modeswitch */ + Pwl map; /* converts dioptres -> lens driver position */ + + CfgParams(); + int read(const libcamera::YamlObject ¶ms); + void initialise(); + }; + + struct ScanRecord { + double focus; + double contrast; + double phase; + double conf; + }; + + struct RegionWeights { + unsigned rows; + unsigned cols; + uint32_t sum; + std::vector w; + + RegionWeights() + : rows(0), cols(0), sum(0), w() {} + }; + + void computeWeights(RegionWeights *wgts, unsigned rows, unsigned cols); + void invalidateWeights(); + bool getPhase(PdafRegions const ®ions, double &phase, double &conf); + double getContrast(const FocusRegions &focusStats); + void doPDAF(double phase, double conf); + bool earlyTerminationByPhase(double phase); + double findPeak(unsigned index) const; + void doScan(double contrast, double phase, double conf); + void doAF(double contrast, double phase, double conf); + void updateLensPosition(); + void startAF(); + void startProgrammedScan(); + void goIdle(); + + /* Configuration and settings */ + CfgParams cfg_; + AfRange range_; + AfSpeed speed_; + AfMode mode_; + bool pauseFlag_; + libcamera::Rectangle statsRegion_; + std::vector windows_; + bool useWindows_; + RegionWeights phaseWeights_; + RegionWeights contrastWeights_; + + /* Working state. */ + ScanState scanState_; + bool initted_; + double ftarget_, fsmooth_; + double prevContrast_; + unsigned skipCount_, stepCount_, dropCount_; + unsigned scanMaxIndex_; + double scanMaxContrast_, scanMinContrast_; + std::vector scanData_; + AfState reportState_; +}; + +} // namespace RPiController diff --git a/src/ipa/rpi/controller/rpi/agc.cpp b/src/ipa/rpi/controller/rpi/agc.cpp new file mode 100644 index 00000000..e6fb7b8d --- /dev/null +++ b/src/ipa/rpi/controller/rpi/agc.cpp @@ -0,0 +1,922 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (C) 2019, Raspberry Pi Ltd + * + * agc.cpp - AGC/AEC control algorithm + */ + +#include +#include +#include + +#include + +#include "../awb_status.h" +#include "../device_status.h" +#include "../histogram.h" +#include "../lux_status.h" +#include "../metadata.h" + +#include "agc.h" + +using namespace RPiController; +using namespace libcamera; +using libcamera::utils::Duration; +using namespace std::literals::chrono_literals; + +LOG_DEFINE_CATEGORY(RPiAgc) + +#define NAME "rpi.agc" + +int AgcMeteringMode::read(const libcamera::YamlObject ¶ms) +{ + const YamlObject &yamlWeights = params["weights"]; + + for (const auto &p : yamlWeights.asList()) { + auto value = p.get(); + if (!value) + return -EINVAL; + weights.push_back(*value); + } + + return 0; +} + +static std::tuple +readMeteringModes(std::map &metering_modes, + const libcamera::YamlObject ¶ms) +{ + std::string first; + int ret; + + for (const auto &[key, value] : params.asDict()) { + AgcMeteringMode meteringMode; + ret = meteringMode.read(value); + if (ret) + return { ret, {} }; + + metering_modes[key] = std::move(meteringMode); + if (first.empty()) + first = key; + } + + return { 0, first }; +} + +int AgcExposureMode::read(const libcamera::YamlObject ¶ms) +{ + auto value = params["shutter"].getList(); + if (!value) + return -EINVAL; + std::transform(value->begin(), value->end(), std::back_inserter(shutter), + [](double v) { return v * 1us; }); + + value = params["gain"].getList(); + if (!value) + return -EINVAL; + gain = std::move(*value); + + if (shutter.size() < 2 || gain.size() < 2) { + LOG(RPiAgc, Error) + << "AgcExposureMode: must have at least two entries in exposure profile"; + return -EINVAL; + } + + if (shutter.size() != gain.size()) { + LOG(RPiAgc, Error) + << "AgcExposureMode: expect same number of exposure and gain entries in exposure profile"; + return -EINVAL; + } + + return 0; +} + +static std::tuple +readExposureModes(std::map &exposureModes, + const libcamera::YamlObject ¶ms) +{ + std::string first; + int ret; + + for (const auto &[key, value] : params.asDict()) { + AgcExposureMode exposureMode; + ret = exposureMode.read(value); + if (ret) + return { ret, {} }; + + exposureModes[key] = std::move(exposureMode); + if (first.empty()) + first = key; + } + + return { 0, first }; +} + +int AgcConstraint::read(const libcamera::YamlObject ¶ms) +{ + std::string boundString = params["bound"].get(""); + transform(boundString.begin(), boundString.end(), + boundString.begin(), ::toupper); + if (boundString != "UPPER" && boundString != "LOWER") { + LOG(RPiAgc, Error) << "AGC constraint type should be UPPER or LOWER"; + return -EINVAL; + } + bound = boundString == "UPPER" ? Bound::UPPER : Bound::LOWER; + + auto value = params["q_lo"].get(); + if (!value) + return -EINVAL; + qLo = *value; + + value = params["q_hi"].get(); + if (!value) + return -EINVAL; + qHi = *value; + + return yTarget.read(params["y_target"]); +} + +static std::tuple +readConstraintMode(const libcamera::YamlObject ¶ms) +{ + AgcConstraintMode mode; + int ret; + + for (const auto &p : params.asList()) { + AgcConstraint constraint; + ret = constraint.read(p); + if (ret) + return { ret, {} }; + + mode.push_back(std::move(constraint)); + } + + return { 0, mode }; +} + +static std::tuple +readConstraintModes(std::map &constraintModes, + const libcamera::YamlObject ¶ms) +{ + std::string first; + int ret; + + for (const auto &[key, value] : params.asDict()) { + std::tie(ret, constraintModes[key]) = readConstraintMode(value); + if (ret) + return { ret, {} }; + + if (first.empty()) + first = key; + } + + return { 0, first }; +} + +int AgcConfig::read(const libcamera::YamlObject ¶ms) +{ + LOG(RPiAgc, Debug) << "AgcConfig"; + int ret; + + std::tie(ret, defaultMeteringMode) = + readMeteringModes(meteringModes, params["metering_modes"]); + if (ret) + return ret; + std::tie(ret, defaultExposureMode) = + readExposureModes(exposureModes, params["exposure_modes"]); + if (ret) + return ret; + std::tie(ret, defaultConstraintMode) = + readConstraintModes(constraintModes, params["constraint_modes"]); + if (ret) + return ret; + + ret = yTarget.read(params["y_target"]); + if (ret) + return ret; + + speed = params["speed"].get(0.2); + startupFrames = params["startup_frames"].get(10); + convergenceFrames = params["convergence_frames"].get(6); + fastReduceThreshold = params["fast_reduce_threshold"].get(0.4); + baseEv = params["base_ev"].get(1.0); + + /* Start with quite a low value as ramping up is easier than ramping down. */ + defaultExposureTime = params["default_exposure_time"].get(1000) * 1us; + defaultAnalogueGain = params["default_analogue_gain"].get(1.0); + + return 0; +} + +Agc::ExposureValues::ExposureValues() + : shutter(0s), analogueGain(0), + totalExposure(0s), totalExposureNoDG(0s) +{ +} + +Agc::Agc(Controller *controller) + : AgcAlgorithm(controller), meteringMode_(nullptr), + exposureMode_(nullptr), constraintMode_(nullptr), + frameCount_(0), lockCount_(0), + lastTargetExposure_(0s), ev_(1.0), flickerPeriod_(0s), + maxShutter_(0s), fixedShutter_(0s), fixedAnalogueGain_(0.0) +{ + memset(&awb_, 0, sizeof(awb_)); + /* + * Setting status_.totalExposureValue_ to zero initially tells us + * it's not been calculated yet (i.e. Process hasn't yet run). + */ + memset(&status_, 0, sizeof(status_)); + status_.ev = ev_; +} + +char const *Agc::name() const +{ + return NAME; +} + +int Agc::read(const libcamera::YamlObject ¶ms) +{ + LOG(RPiAgc, Debug) << "Agc"; + + int ret = config_.read(params); + if (ret) + return ret; + + const Size &size = getHardwareConfig().agcZoneWeights; + for (auto const &modes : config_.meteringModes) { + if (modes.second.weights.size() != size.width * size.height) { + LOG(RPiAgc, Error) << "AgcMeteringMode: Incorrect number of weights"; + return -EINVAL; + } + } + + /* + * Set the config's defaults (which are the first ones it read) as our + * current modes, until someone changes them. (they're all known to + * exist at this point) + */ + meteringModeName_ = config_.defaultMeteringMode; + meteringMode_ = &config_.meteringModes[meteringModeName_]; + exposureModeName_ = config_.defaultExposureMode; + exposureMode_ = &config_.exposureModes[exposureModeName_]; + constraintModeName_ = config_.defaultConstraintMode; + constraintMode_ = &config_.constraintModes[constraintModeName_]; + /* Set up the "last shutter/gain" values, in case AGC starts "disabled". */ + status_.shutterTime = config_.defaultExposureTime; + status_.analogueGain = config_.defaultAnalogueGain; + return 0; +} + +void Agc::disableAuto() +{ + fixedShutter_ = status_.shutterTime; + fixedAnalogueGain_ = status_.analogueGain; +} + +void Agc::enableAuto() +{ + fixedShutter_ = 0s; + fixedAnalogueGain_ = 0; +} + +unsigned int Agc::getConvergenceFrames() const +{ + /* + * If shutter and gain have been explicitly set, there is no + * convergence to happen, so no need to drop any frames - return zero. + */ + if (fixedShutter_ && fixedAnalogueGain_) + return 0; + else + return config_.convergenceFrames; +} + +void Agc::setEv(double ev) +{ + ev_ = ev; +} + +void Agc::setFlickerPeriod(Duration flickerPeriod) +{ + flickerPeriod_ = flickerPeriod; +} + +void Agc::setMaxShutter(Duration maxShutter) +{ + maxShutter_ = maxShutter; +} + +void Agc::setFixedShutter(Duration fixedShutter) +{ + fixedShutter_ = fixedShutter; + /* Set this in case someone calls disableAuto() straight after. */ + status_.shutterTime = limitShutter(fixedShutter_); +} + +void Agc::setFixedAnalogueGain(double fixedAnalogueGain) +{ + fixedAnalogueGain_ = fixedAnalogueGain; + /* Set this in case someone calls disableAuto() straight after. */ + status_.analogueGain = limitGain(fixedAnalogueGain); +} + +void Agc::setMeteringMode(std::string const &meteringModeName) +{ + meteringModeName_ = meteringModeName; +} + +void Agc::setExposureMode(std::string const &exposureModeName) +{ + exposureModeName_ = exposureModeName; +} + +void Agc::setConstraintMode(std::string const &constraintModeName) +{ + constraintModeName_ = constraintModeName; +} + +void Agc::switchMode(CameraMode const &cameraMode, + Metadata *metadata) +{ + /* AGC expects the mode sensitivity always to be non-zero. */ + ASSERT(cameraMode.sensitivity); + + housekeepConfig(); + + /* + * Store the mode in the local state. We must cache the sensitivity of + * of the previous mode for the calculations below. + */ + double lastSensitivity = mode_.sensitivity; + mode_ = cameraMode; + + Duration fixedShutter = limitShutter(fixedShutter_); + if (fixedShutter && fixedAnalogueGain_) { + /* We're going to reset the algorithm here with these fixed values. */ + + fetchAwbStatus(metadata); + double minColourGain = std::min({ awb_.gainR, awb_.gainG, awb_.gainB, 1.0 }); + ASSERT(minColourGain != 0.0); + + /* This is the equivalent of computeTargetExposure and applyDigitalGain. */ + target_.totalExposureNoDG = fixedShutter_ * fixedAnalogueGain_; + target_.totalExposure = target_.totalExposureNoDG / minColourGain; + + /* Equivalent of filterExposure. This resets any "history". */ + filtered_ = target_; + + /* Equivalent of divideUpExposure. */ + filtered_.shutter = fixedShutter; + filtered_.analogueGain = fixedAnalogueGain_; + } else if (status_.totalExposureValue) { + /* + * On a mode switch, various things could happen: + * - the exposure profile might change + * - a fixed exposure or gain might be set + * - the new mode's sensitivity might be different + * We cope with the last of these by scaling the target values. After + * that we just need to re-divide the exposure/gain according to the + * current exposure profile, which takes care of everything else. + */ + + double ratio = lastSensitivity / cameraMode.sensitivity; + target_.totalExposureNoDG *= ratio; + target_.totalExposure *= ratio; + filtered_.totalExposureNoDG *= ratio; + filtered_.totalExposure *= ratio; + + divideUpExposure(); + } else { + /* + * We come through here on startup, when at least one of the shutter + * or gain has not been fixed. We must still write those values out so + * that they will be applied immediately. We supply some arbitrary defaults + * for any that weren't set. + */ + + /* Equivalent of divideUpExposure. */ + filtered_.shutter = fixedShutter ? fixedShutter : config_.defaultExposureTime; + filtered_.analogueGain = fixedAnalogueGain_ ? fixedAnalogueGain_ : config_.defaultAnalogueGain; + } + + writeAndFinish(metadata, false); +} + +void Agc::prepare(Metadata *imageMetadata) +{ + Duration totalExposureValue = status_.totalExposureValue; + AgcStatus delayedStatus; + + if (!imageMetadata->get("agc.delayed_status", delayedStatus)) + totalExposureValue = delayedStatus.totalExposureValue; + + status_.digitalGain = 1.0; + fetchAwbStatus(imageMetadata); /* always fetch it so that Process knows it's been done */ + + if (status_.totalExposureValue) { + /* Process has run, so we have meaningful values. */ + DeviceStatus deviceStatus; + if (imageMetadata->get("device.status", deviceStatus) == 0) { + Duration actualExposure = deviceStatus.shutterSpeed * + deviceStatus.analogueGain; + if (actualExposure) { + status_.digitalGain = totalExposureValue / actualExposure; + LOG(RPiAgc, Debug) << "Want total exposure " << totalExposureValue; + /* + * Never ask for a gain < 1.0, and also impose + * some upper limit. Make it customisable? + */ + status_.digitalGain = std::max(1.0, std::min(status_.digitalGain, 4.0)); + LOG(RPiAgc, Debug) << "Actual exposure " << actualExposure; + LOG(RPiAgc, Debug) << "Use digitalGain " << status_.digitalGain; + LOG(RPiAgc, Debug) << "Effective exposure " + << actualExposure * status_.digitalGain; + /* Decide whether AEC/AGC has converged. */ + updateLockStatus(deviceStatus); + } + } else + LOG(RPiAgc, Warning) << name() << ": no device metadata"; + imageMetadata->set("agc.status", status_); + } +} + +void Agc::process(StatisticsPtr &stats, Metadata *imageMetadata) +{ + frameCount_++; + /* + * First a little bit of housekeeping, fetching up-to-date settings and + * configuration, that kind of thing. + */ + housekeepConfig(); + /* Get the current exposure values for the frame that's just arrived. */ + fetchCurrentExposure(imageMetadata); + /* Compute the total gain we require relative to the current exposure. */ + double gain, targetY; + computeGain(stats, imageMetadata, gain, targetY); + /* Now compute the target (final) exposure which we think we want. */ + computeTargetExposure(gain); + /* + * Some of the exposure has to be applied as digital gain, so work out + * what that is. This function also tells us whether it's decided to + * "desaturate" the image more quickly. + */ + bool desaturate = applyDigitalGain(gain, targetY); + /* The results have to be filtered so as not to change too rapidly. */ + filterExposure(desaturate); + /* + * The last thing is to divide up the exposure value into a shutter time + * and analogue gain, according to the current exposure mode. + */ + divideUpExposure(); + /* Finally advertise what we've done. */ + writeAndFinish(imageMetadata, desaturate); +} + +void Agc::updateLockStatus(DeviceStatus const &deviceStatus) +{ + const double errorFactor = 0.10; /* make these customisable? */ + const int maxLockCount = 5; + /* Reset "lock count" when we exceed this multiple of errorFactor */ + const double resetMargin = 1.5; + + /* Add 200us to the exposure time error to allow for line quantisation. */ + Duration exposureError = lastDeviceStatus_.shutterSpeed * errorFactor + 200us; + double gainError = lastDeviceStatus_.analogueGain * errorFactor; + Duration targetError = lastTargetExposure_ * errorFactor; + + /* + * Note that we don't know the exposure/gain limits of the sensor, so + * the values we keep requesting may be unachievable. For this reason + * we only insist that we're close to values in the past few frames. + */ + if (deviceStatus.shutterSpeed > lastDeviceStatus_.shutterSpeed - exposureError && + deviceStatus.shutterSpeed < lastDeviceStatus_.shutterSpeed + exposureError && + deviceStatus.analogueGain > lastDeviceStatus_.analogueGain - gainError && + deviceStatus.analogueGain < lastDeviceStatus_.analogueGain + gainError && + status_.targetExposureValue > lastTargetExposure_ - targetError && + status_.targetExposureValue < lastTargetExposure_ + targetError) + lockCount_ = std::min(lockCount_ + 1, maxLockCount); + else if (deviceStatus.shutterSpeed < lastDeviceStatus_.shutterSpeed - resetMargin * exposureError || + deviceStatus.shutterSpeed > lastDeviceStatus_.shutterSpeed + resetMargin * exposureError || + deviceStatus.analogueGain < lastDeviceStatus_.analogueGain - resetMargin * gainError || + deviceStatus.analogueGain > lastDeviceStatus_.analogueGain + resetMargin * gainError || + status_.targetExposureValue < lastTargetExposure_ - resetMargin * targetError || + status_.targetExposureValue > lastTargetExposure_ + resetMargin * targetError) + lockCount_ = 0; + + lastDeviceStatus_ = deviceStatus; + lastTargetExposure_ = status_.targetExposureValue; + + LOG(RPiAgc, Debug) << "Lock count updated to " << lockCount_; + status_.locked = lockCount_ == maxLockCount; +} + +static void copyString(std::string const &s, char *d, size_t size) +{ + size_t length = s.copy(d, size - 1); + d[length] = '\0'; +} + +void Agc::housekeepConfig() +{ + /* First fetch all the up-to-date settings, so no one else has to do it. */ + status_.ev = ev_; + status_.fixedShutter = limitShutter(fixedShutter_); + status_.fixedAnalogueGain = fixedAnalogueGain_; + status_.flickerPeriod = flickerPeriod_; + LOG(RPiAgc, Debug) << "ev " << status_.ev << " fixedShutter " + << status_.fixedShutter << " fixedAnalogueGain " + << status_.fixedAnalogueGain; + /* + * Make sure the "mode" pointers point to the up-to-date things, if + * they've changed. + */ + if (strcmp(meteringModeName_.c_str(), status_.meteringMode)) { + auto it = config_.meteringModes.find(meteringModeName_); + if (it == config_.meteringModes.end()) + LOG(RPiAgc, Fatal) << "No metering mode " << meteringModeName_; + meteringMode_ = &it->second; + copyString(meteringModeName_, status_.meteringMode, + sizeof(status_.meteringMode)); + } + if (strcmp(exposureModeName_.c_str(), status_.exposureMode)) { + auto it = config_.exposureModes.find(exposureModeName_); + if (it == config_.exposureModes.end()) + LOG(RPiAgc, Fatal) << "No exposure profile " << exposureModeName_; + exposureMode_ = &it->second; + copyString(exposureModeName_, status_.exposureMode, + sizeof(status_.exposureMode)); + } + if (strcmp(constraintModeName_.c_str(), status_.constraintMode)) { + auto it = + config_.constraintModes.find(constraintModeName_); + if (it == config_.constraintModes.end()) + LOG(RPiAgc, Fatal) << "No constraint list " << constraintModeName_; + constraintMode_ = &it->second; + copyString(constraintModeName_, status_.constraintMode, + sizeof(status_.constraintMode)); + } + LOG(RPiAgc, Debug) << "exposureMode " + << exposureModeName_ << " constraintMode " + << constraintModeName_ << " meteringMode " + << meteringModeName_; +} + +void Agc::fetchCurrentExposure(Metadata *imageMetadata) +{ + std::unique_lock lock(*imageMetadata); + DeviceStatus *deviceStatus = + imageMetadata->getLocked("device.status"); + if (!deviceStatus) + LOG(RPiAgc, Fatal) << "No device metadata"; + current_.shutter = deviceStatus->shutterSpeed; + current_.analogueGain = deviceStatus->analogueGain; + AgcStatus *agcStatus = + imageMetadata->getLocked("agc.status"); + current_.totalExposure = agcStatus ? agcStatus->totalExposureValue : 0s; + current_.totalExposureNoDG = current_.shutter * current_.analogueGain; +} + +void Agc::fetchAwbStatus(Metadata *imageMetadata) +{ + awb_.gainR = 1.0; /* in case not found in metadata */ + awb_.gainG = 1.0; + awb_.gainB = 1.0; + if (imageMetadata->get("awb.status", awb_) != 0) + LOG(RPiAgc, Debug) << "No AWB status found"; +} + +static double computeInitialY(StatisticsPtr &stats, AwbStatus const &awb, + std::vector &weights, double gain) +{ + constexpr uint64_t maxVal = 1 << Statistics::NormalisationFactorPow2; + + ASSERT(weights.size() == stats->agcRegions.numRegions()); + + /* + * Note how the calculation below means that equal weights give you + * "average" metering (i.e. all pixels equally important). + */ + double rSum = 0, gSum = 0, bSum = 0, pixelSum = 0; + for (unsigned int i = 0; i < stats->agcRegions.numRegions(); i++) { + auto ®ion = stats->agcRegions.get(i); + double rAcc = std::min(region.val.rSum * gain, (maxVal - 1) * region.counted); + double gAcc = std::min(region.val.gSum * gain, (maxVal - 1) * region.counted); + double bAcc = std::min(region.val.bSum * gain, (maxVal - 1) * region.counted); + rSum += rAcc * weights[i]; + gSum += gAcc * weights[i]; + bSum += bAcc * weights[i]; + pixelSum += region.counted * weights[i]; + } + if (pixelSum == 0.0) { + LOG(RPiAgc, Warning) << "computeInitialY: pixelSum is zero"; + return 0; + } + double ySum = rSum * awb.gainR * .299 + + gSum * awb.gainG * .587 + + bSum * awb.gainB * .114; + return ySum / pixelSum / maxVal; +} + +/* + * We handle extra gain through EV by adjusting our Y targets. However, you + * simply can't monitor histograms once they get very close to (or beyond!) + * saturation, so we clamp the Y targets to this value. It does mean that EV + * increases don't necessarily do quite what you might expect in certain + * (contrived) cases. + */ + +static constexpr double EvGainYTargetLimit = 0.9; + +static double constraintComputeGain(AgcConstraint &c, const Histogram &h, double lux, + double evGain, double &targetY) +{ + targetY = c.yTarget.eval(c.yTarget.domain().clip(lux)); + targetY = std::min(EvGainYTargetLimit, targetY * evGain); + double iqm = h.interQuantileMean(c.qLo, c.qHi); + return (targetY * h.bins()) / iqm; +} + +void Agc::computeGain(StatisticsPtr &statistics, Metadata *imageMetadata, + double &gain, double &targetY) +{ + struct LuxStatus lux = {}; + lux.lux = 400; /* default lux level to 400 in case no metadata found */ + if (imageMetadata->get("lux.status", lux) != 0) + LOG(RPiAgc, Warning) << "No lux level found"; + const Histogram &h = statistics->yHist; + double evGain = status_.ev * config_.baseEv; + /* + * The initial gain and target_Y come from some of the regions. After + * that we consider the histogram constraints. + */ + targetY = config_.yTarget.eval(config_.yTarget.domain().clip(lux.lux)); + targetY = std::min(EvGainYTargetLimit, targetY * evGain); + + /* + * Do this calculation a few times as brightness increase can be + * non-linear when there are saturated regions. + */ + gain = 1.0; + for (int i = 0; i < 8; i++) { + double initialY = computeInitialY(statistics, awb_, meteringMode_->weights, gain); + double extraGain = std::min(10.0, targetY / (initialY + .001)); + gain *= extraGain; + LOG(RPiAgc, Debug) << "Initial Y " << initialY << " target " << targetY + << " gives gain " << gain; + if (extraGain < 1.01) /* close enough */ + break; + } + + for (auto &c : *constraintMode_) { + double newTargetY; + double newGain = constraintComputeGain(c, h, lux.lux, evGain, newTargetY); + LOG(RPiAgc, Debug) << "Constraint has target_Y " + << newTargetY << " giving gain " << newGain; + if (c.bound == AgcConstraint::Bound::LOWER && newGain > gain) { + LOG(RPiAgc, Debug) << "Lower bound constraint adopted"; + gain = newGain; + targetY = newTargetY; + } else if (c.bound == AgcConstraint::Bound::UPPER && newGain < gain) { + LOG(RPiAgc, Debug) << "Upper bound constraint adopted"; + gain = newGain; + targetY = newTargetY; + } + } + LOG(RPiAgc, Debug) << "Final gain " << gain << " (target_Y " << targetY << " ev " + << status_.ev << " base_ev " << config_.baseEv + << ")"; +} + +void Agc::computeTargetExposure(double gain) +{ + if (status_.fixedShutter && status_.fixedAnalogueGain) { + /* + * When ag and shutter are both fixed, we need to drive the + * total exposure so that we end up with a digital gain of at least + * 1/minColourGain. Otherwise we'd desaturate channels causing + * white to go cyan or magenta. + */ + double minColourGain = std::min({ awb_.gainR, awb_.gainG, awb_.gainB, 1.0 }); + ASSERT(minColourGain != 0.0); + target_.totalExposure = + status_.fixedShutter * status_.fixedAnalogueGain / minColourGain; + } else { + /* + * The statistics reflect the image without digital gain, so the final + * total exposure we're aiming for is: + */ + target_.totalExposure = current_.totalExposureNoDG * gain; + /* The final target exposure is also limited to what the exposure mode allows. */ + Duration maxShutter = status_.fixedShutter + ? status_.fixedShutter + : exposureMode_->shutter.back(); + maxShutter = limitShutter(maxShutter); + Duration maxTotalExposure = + maxShutter * + (status_.fixedAnalogueGain != 0.0 + ? status_.fixedAnalogueGain + : exposureMode_->gain.back()); + target_.totalExposure = std::min(target_.totalExposure, maxTotalExposure); + } + LOG(RPiAgc, Debug) << "Target totalExposure " << target_.totalExposure; +} + +bool Agc::applyDigitalGain(double gain, double targetY) +{ + double minColourGain = std::min({ awb_.gainR, awb_.gainG, awb_.gainB, 1.0 }); + ASSERT(minColourGain != 0.0); + double dg = 1.0 / minColourGain; + /* + * I think this pipeline subtracts black level and rescales before we + * get the stats, so no need to worry about it. + */ + LOG(RPiAgc, Debug) << "after AWB, target dg " << dg << " gain " << gain + << " target_Y " << targetY; + /* + * Finally, if we're trying to reduce exposure but the target_Y is + * "close" to 1.0, then the gain computed for that constraint will be + * only slightly less than one, because the measured Y can never be + * larger than 1.0. When this happens, demand a large digital gain so + * that the exposure can be reduced, de-saturating the image much more + * quickly (and we then approach the correct value more quickly from + * below). + */ + bool desaturate = targetY > config_.fastReduceThreshold && + gain < sqrt(targetY); + if (desaturate) + dg /= config_.fastReduceThreshold; + LOG(RPiAgc, Debug) << "Digital gain " << dg << " desaturate? " << desaturate; + target_.totalExposureNoDG = target_.totalExposure / dg; + LOG(RPiAgc, Debug) << "Target totalExposureNoDG " << target_.totalExposureNoDG; + return desaturate; +} + +void Agc::filterExposure(bool desaturate) +{ + double speed = config_.speed; + /* + * AGC adapts instantly if both shutter and gain are directly specified + * or we're in the startup phase. + */ + if ((status_.fixedShutter && status_.fixedAnalogueGain) || + frameCount_ <= config_.startupFrames) + speed = 1.0; + if (!filtered_.totalExposure) { + filtered_.totalExposure = target_.totalExposure; + filtered_.totalExposureNoDG = target_.totalExposureNoDG; + } else { + /* + * If close to the result go faster, to save making so many + * micro-adjustments on the way. (Make this customisable?) + */ + if (filtered_.totalExposure < 1.2 * target_.totalExposure && + filtered_.totalExposure > 0.8 * target_.totalExposure) + speed = sqrt(speed); + filtered_.totalExposure = speed * target_.totalExposure + + filtered_.totalExposure * (1.0 - speed); + /* + * When desaturing, take a big jump down in totalExposureNoDG, + * which we'll hide with digital gain. + */ + if (desaturate) + filtered_.totalExposureNoDG = + target_.totalExposureNoDG; + else + filtered_.totalExposureNoDG = + speed * target_.totalExposureNoDG + + filtered_.totalExposureNoDG * (1.0 - speed); + } + /* + * We can't let the totalExposureNoDG exposure deviate too far below the + * total exposure, as there might not be enough digital gain available + * in the ISP to hide it (which will cause nasty oscillation). + */ + if (filtered_.totalExposureNoDG < + filtered_.totalExposure * config_.fastReduceThreshold) + filtered_.totalExposureNoDG = filtered_.totalExposure * config_.fastReduceThreshold; + LOG(RPiAgc, Debug) << "After filtering, totalExposure " << filtered_.totalExposure + << " no dg " << filtered_.totalExposureNoDG; +} + +void Agc::divideUpExposure() +{ + /* + * Sending the fixed shutter/gain cases through the same code may seem + * unnecessary, but it will make more sense when extend this to cover + * variable aperture. + */ + Duration exposureValue = filtered_.totalExposureNoDG; + Duration shutterTime; + double analogueGain; + shutterTime = status_.fixedShutter ? status_.fixedShutter + : exposureMode_->shutter[0]; + shutterTime = limitShutter(shutterTime); + analogueGain = status_.fixedAnalogueGain != 0.0 ? status_.fixedAnalogueGain + : exposureMode_->gain[0]; + analogueGain = limitGain(analogueGain); + if (shutterTime * analogueGain < exposureValue) { + for (unsigned int stage = 1; + stage < exposureMode_->gain.size(); stage++) { + if (!status_.fixedShutter) { + Duration stageShutter = + limitShutter(exposureMode_->shutter[stage]); + if (stageShutter * analogueGain >= exposureValue) { + shutterTime = exposureValue / analogueGain; + break; + } + shutterTime = stageShutter; + } + if (status_.fixedAnalogueGain == 0.0) { + if (exposureMode_->gain[stage] * shutterTime >= exposureValue) { + analogueGain = exposureValue / shutterTime; + break; + } + analogueGain = exposureMode_->gain[stage]; + analogueGain = limitGain(analogueGain); + } + } + } + LOG(RPiAgc, Debug) << "Divided up shutter and gain are " << shutterTime << " and " + << analogueGain; + /* + * Finally adjust shutter time for flicker avoidance (require both + * shutter and gain not to be fixed). + */ + if (!status_.fixedShutter && !status_.fixedAnalogueGain && + status_.flickerPeriod) { + int flickerPeriods = shutterTime / status_.flickerPeriod; + if (flickerPeriods) { + Duration newShutterTime = flickerPeriods * status_.flickerPeriod; + analogueGain *= shutterTime / newShutterTime; + /* + * We should still not allow the ag to go over the + * largest value in the exposure mode. Note that this + * may force more of the total exposure into the digital + * gain as a side-effect. + */ + analogueGain = std::min(analogueGain, exposureMode_->gain.back()); + analogueGain = limitGain(analogueGain); + shutterTime = newShutterTime; + } + LOG(RPiAgc, Debug) << "After flicker avoidance, shutter " + << shutterTime << " gain " << analogueGain; + } + filtered_.shutter = shutterTime; + filtered_.analogueGain = analogueGain; +} + +void Agc::writeAndFinish(Metadata *imageMetadata, bool desaturate) +{ + status_.totalExposureValue = filtered_.totalExposure; + status_.targetExposureValue = desaturate ? 0s : target_.totalExposureNoDG; + status_.shutterTime = filtered_.shutter; + status_.analogueGain = filtered_.analogueGain; + /* + * Write to metadata as well, in case anyone wants to update the camera + * immediately. + */ + imageMetadata->set("agc.status", status_); + LOG(RPiAgc, Debug) << "Output written, total exposure requested is " + << filtered_.totalExposure; + LOG(RPiAgc, Debug) << "Camera exposure update: shutter time " << filtered_.shutter + << " analogue gain " << filtered_.analogueGain; +} + +Duration Agc::limitShutter(Duration shutter) +{ + /* + * shutter == 0 is a special case for fixed shutter values, and must pass + * through unchanged + */ + if (!shutter) + return shutter; + + shutter = std::clamp(shutter, mode_.minShutter, maxShutter_); + return shutter; +} + +double Agc::limitGain(double gain) const +{ + /* + * Only limit the lower bounds of the gain value to what the sensor limits. + * The upper bound on analogue gain will be made up with additional digital + * gain applied by the ISP. + * + * gain == 0.0 is a special case for fixed shutter values, and must pass + * through unchanged + */ + if (!gain) + return gain; + + gain = std::max(gain, mode_.minAnalogueGain); + return gain; +} + +/* Register algorithm with the system. */ +static Algorithm *create(Controller *controller) +{ + return (Algorithm *)new Agc(controller); +} +static RegisterAlgorithm reg(NAME, &create); diff --git a/src/ipa/rpi/controller/rpi/agc.h b/src/ipa/rpi/controller/rpi/agc.h new file mode 100644 index 00000000..4e5f272f --- /dev/null +++ b/src/ipa/rpi/controller/rpi/agc.h @@ -0,0 +1,133 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (C) 2019, Raspberry Pi Ltd + * + * agc.h - AGC/AEC control algorithm + */ +#pragma once + +#include +#include + +#include + +#include "../agc_algorithm.h" +#include "../agc_status.h" +#include "../pwl.h" + +/* This is our implementation of AGC. */ + +namespace RPiController { + +struct AgcMeteringMode { + std::vector weights; + int read(const libcamera::YamlObject ¶ms); +}; + +struct AgcExposureMode { + std::vector shutter; + std::vector gain; + int read(const libcamera::YamlObject ¶ms); +}; + +struct AgcConstraint { + enum class Bound { LOWER = 0, UPPER = 1 }; + Bound bound; + double qLo; + double qHi; + Pwl yTarget; + int read(const libcamera::YamlObject ¶ms); +}; + +typedef std::vector AgcConstraintMode; + +struct AgcConfig { + int read(const libcamera::YamlObject ¶ms); + std::map meteringModes; + std::map exposureModes; + std::map constraintModes; + Pwl yTarget; + double speed; + uint16_t startupFrames; + unsigned int convergenceFrames; + double maxChange; + double minChange; + double fastReduceThreshold; + double speedUpThreshold; + std::string defaultMeteringMode; + std::string defaultExposureMode; + std::string defaultConstraintMode; + double baseEv; + libcamera::utils::Duration defaultExposureTime; + double defaultAnalogueGain; +}; + +class Agc : public AgcAlgorithm +{ +public: + Agc(Controller *controller); + char const *name() const override; + int read(const libcamera::YamlObject ¶ms) override; + unsigned int getConvergenceFrames() const override; + void setEv(double ev) override; + void setFlickerPeriod(libcamera::utils::Duration flickerPeriod) override; + void setMaxShutter(libcamera::utils::Duration maxShutter) override; + void setFixedShutter(libcamera::utils::Duration fixedShutter) override; + void setFixedAnalogueGain(double fixedAnalogueGain) override; + void setMeteringMode(std::string const &meteringModeName) override; + void setExposureMode(std::string const &exposureModeName) override; + void setConstraintMode(std::string const &contraintModeName) override; + void enableAuto() override; + void disableAuto() override; + void switchMode(CameraMode const &cameraMode, Metadata *metadata) override; + void prepare(Metadata *imageMetadata) override; + void process(StatisticsPtr &stats, Metadata *imageMetadata) override; + +private: + void updateLockStatus(DeviceStatus const &deviceStatus); + AgcConfig config_; + void housekeepConfig(); + void fetchCurrentExposure(Metadata *imageMetadata); + void fetchAwbStatus(Metadata *imageMetadata); + void computeGain(StatisticsPtr &statistics, Metadata *imageMetadata, + double &gain, double &targetY); + void computeTargetExposure(double gain); + bool applyDigitalGain(double gain, double targetY); + void filterExposure(bool desaturate); + void divideUpExposure(); + void writeAndFinish(Metadata *imageMetadata, bool desaturate); + libcamera::utils::Duration limitShutter(libcamera::utils::Duration shutter); + double limitGain(double gain) const; + AgcMeteringMode *meteringMode_; + AgcExposureMode *exposureMode_; + AgcConstraintMode *constraintMode_; + CameraMode mode_; + uint64_t frameCount_; + AwbStatus awb_; + struct ExposureValues { + ExposureValues(); + + libcamera::utils::Duration shutter; + double analogueGain; + libcamera::utils::Duration totalExposure; + libcamera::utils::Duration totalExposureNoDG; /* without digital gain */ + }; + ExposureValues current_; /* values for the current frame */ + ExposureValues target_; /* calculate the values we want here */ + ExposureValues filtered_; /* these values are filtered towards target */ + AgcStatus status_; + int lockCount_; + DeviceStatus lastDeviceStatus_; + libcamera::utils::Duration lastTargetExposure_; + /* Below here the "settings" that applications can change. */ + std::string meteringModeName_; + std::string exposureModeName_; + std::string constraintModeName_; + double ev_; + libcamera::utils::Duration flickerPeriod_; + libcamera::utils::Duration maxShutter_; + libcamera::utils::Duration fixedShutter_; + double fixedAnalogueGain_; +}; + +} /* namespace RPiController */ diff --git a/src/ipa/rpi/controller/rpi/alsc.cpp b/src/ipa/rpi/controller/rpi/alsc.cpp new file mode 100644 index 00000000..3a2e8fe0 --- /dev/null +++ b/src/ipa/rpi/controller/rpi/alsc.cpp @@ -0,0 +1,865 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (C) 2019, Raspberry Pi Ltd + * + * alsc.cpp - ALSC (auto lens shading correction) control algorithm + */ + +#include +#include +#include +#include + +#include +#include + +#include "../awb_status.h" +#include "alsc.h" + +/* Raspberry Pi ALSC (Auto Lens Shading Correction) algorithm. */ + +using namespace RPiController; +using namespace libcamera; + +LOG_DEFINE_CATEGORY(RPiAlsc) + +#define NAME "rpi.alsc" + +static const double InsufficientData = -1.0; + +Alsc::Alsc(Controller *controller) + : Algorithm(controller) +{ + asyncAbort_ = asyncStart_ = asyncStarted_ = asyncFinished_ = false; + asyncThread_ = std::thread(std::bind(&Alsc::asyncFunc, this)); +} + +Alsc::~Alsc() +{ + { + std::lock_guard lock(mutex_); + asyncAbort_ = true; + } + asyncSignal_.notify_one(); + asyncThread_.join(); +} + +char const *Alsc::name() const +{ + return NAME; +} + +static int generateLut(Array2D &lut, const libcamera::YamlObject ¶ms) +{ + /* These must be signed ints for the co-ordinate calculations below. */ + int X = lut.dimensions().width, Y = lut.dimensions().height; + double cstrength = params["corner_strength"].get(2.0); + if (cstrength <= 1.0) { + LOG(RPiAlsc, Error) << "corner_strength must be > 1.0"; + return -EINVAL; + } + + double asymmetry = params["asymmetry"].get(1.0); + if (asymmetry < 0) { + LOG(RPiAlsc, Error) << "asymmetry must be >= 0"; + return -EINVAL; + } + + double f1 = cstrength - 1, f2 = 1 + sqrt(cstrength); + double R2 = X * Y / 4 * (1 + asymmetry * asymmetry); + int num = 0; + for (int y = 0; y < Y; y++) { + for (int x = 0; x < X; x++) { + double dy = y - Y / 2 + 0.5, + dx = (x - X / 2 + 0.5) * asymmetry; + double r2 = (dx * dx + dy * dy) / R2; + lut[num++] = + (f1 * r2 + f2) * (f1 * r2 + f2) / + (f2 * f2); /* this reproduces the cos^4 rule */ + } + } + return 0; +} + +static int readLut(Array2D &lut, const libcamera::YamlObject ¶ms) +{ + if (params.size() != lut.size()) { + LOG(RPiAlsc, Error) << "Invalid number of entries in LSC table"; + return -EINVAL; + } + + int num = 0; + for (const auto &p : params.asList()) { + auto value = p.get(); + if (!value) + return -EINVAL; + lut[num++] = *value; + } + + return 0; +} + +static int readCalibrations(std::vector &calibrations, + const libcamera::YamlObject ¶ms, + std::string const &name, const Size &size) +{ + if (params.contains(name)) { + double lastCt = 0; + for (const auto &p : params[name].asList()) { + auto value = p["ct"].get(); + if (!value) + return -EINVAL; + double ct = *value; + if (ct <= lastCt) { + LOG(RPiAlsc, Error) + << "Entries in " << name << " must be in increasing ct order"; + return -EINVAL; + } + AlscCalibration calibration; + calibration.ct = lastCt = ct; + + const libcamera::YamlObject &table = p["table"]; + if (table.size() != size.width * size.height) { + LOG(RPiAlsc, Error) + << "Incorrect number of values for ct " + << ct << " in " << name; + return -EINVAL; + } + + int num = 0; + calibration.table.resize(size); + for (const auto &elem : table.asList()) { + value = elem.get(); + if (!value) + return -EINVAL; + calibration.table[num++] = *value; + } + + calibrations.push_back(std::move(calibration)); + LOG(RPiAlsc, Debug) + << "Read " << name << " calibration for ct " << ct; + } + } + return 0; +} + +int Alsc::read(const libcamera::YamlObject ¶ms) +{ + config_.tableSize = getHardwareConfig().awbRegions; + config_.framePeriod = params["frame_period"].get(12); + config_.startupFrames = params["startup_frames"].get(10); + config_.speed = params["speed"].get(0.05); + double sigma = params["sigma"].get(0.01); + config_.sigmaCr = params["sigma_Cr"].get(sigma); + config_.sigmaCb = params["sigma_Cb"].get(sigma); + config_.minCount = params["min_count"].get(10.0); + config_.minG = params["min_G"].get(50); + config_.omega = params["omega"].get(1.3); + config_.nIter = params["n_iter"].get(config_.tableSize.width + config_.tableSize.height); + config_.luminanceStrength = + params["luminance_strength"].get(1.0); + + config_.luminanceLut.resize(config_.tableSize, 1.0); + int ret = 0; + + if (params.contains("corner_strength")) + ret = generateLut(config_.luminanceLut, params); + else if (params.contains("luminance_lut")) + ret = readLut(config_.luminanceLut, params["luminance_lut"]); + else + LOG(RPiAlsc, Warning) + << "no luminance table - assume unity everywhere"; + if (ret) + return ret; + + ret = readCalibrations(config_.calibrationsCr, params, "calibrations_Cr", + config_.tableSize); + if (ret) + return ret; + ret = readCalibrations(config_.calibrationsCb, params, "calibrations_Cb", + config_.tableSize); + if (ret) + return ret; + + config_.defaultCt = params["default_ct"].get(4500.0); + config_.threshold = params["threshold"].get(1e-3); + config_.lambdaBound = params["lambda_bound"].get(0.05); + + return 0; +} + +static double getCt(Metadata *metadata, double defaultCt); +static void getCalTable(double ct, std::vector const &calibrations, + Array2D &calTable); +static void resampleCalTable(const Array2D &calTableIn, CameraMode const &cameraMode, + Array2D &calTableOut); +static void compensateLambdasForCal(const Array2D &calTable, + const Array2D &oldLambdas, + Array2D &newLambdas); +static void addLuminanceToTables(std::array, 3> &results, + const Array2D &lambdaR, double lambdaG, + const Array2D &lambdaB, + const Array2D &luminanceLut, + double luminanceStrength); + +void Alsc::initialise() +{ + frameCount2_ = frameCount_ = framePhase_ = 0; + firstTime_ = true; + ct_ = config_.defaultCt; + + const size_t XY = config_.tableSize.width * config_.tableSize.height; + + for (auto &r : syncResults_) + r.resize(config_.tableSize); + for (auto &r : prevSyncResults_) + r.resize(config_.tableSize); + for (auto &r : asyncResults_) + r.resize(config_.tableSize); + + luminanceTable_.resize(config_.tableSize); + asyncLambdaR_.resize(config_.tableSize); + asyncLambdaB_.resize(config_.tableSize); + /* The lambdas are initialised in the SwitchMode. */ + lambdaR_.resize(config_.tableSize); + lambdaB_.resize(config_.tableSize); + + /* Temporaries for the computations, but sensible to allocate this up-front! */ + for (auto &c : tmpC_) + c.resize(config_.tableSize); + for (auto &m : tmpM_) + m.resize(XY); +} + +void Alsc::waitForAysncThread() +{ + if (asyncStarted_) { + asyncStarted_ = false; + std::unique_lock lock(mutex_); + syncSignal_.wait(lock, [&] { + return asyncFinished_; + }); + asyncFinished_ = false; + } +} + +static bool compareModes(CameraMode const &cm0, CameraMode const &cm1) +{ + /* + * Return true if the modes crop from the sensor significantly differently, + * or if the user transform has changed. + */ + if (cm0.transform != cm1.transform) + return true; + int leftDiff = abs(cm0.cropX - cm1.cropX); + int topDiff = abs(cm0.cropY - cm1.cropY); + int rightDiff = fabs(cm0.cropX + cm0.scaleX * cm0.width - + cm1.cropX - cm1.scaleX * cm1.width); + int bottomDiff = fabs(cm0.cropY + cm0.scaleY * cm0.height - + cm1.cropY - cm1.scaleY * cm1.height); + /* + * These thresholds are a rather arbitrary amount chosen to trigger + * when carrying on with the previously calculated tables might be + * worse than regenerating them (but without the adaptive algorithm). + */ + int thresholdX = cm0.sensorWidth >> 4; + int thresholdY = cm0.sensorHeight >> 4; + return leftDiff > thresholdX || rightDiff > thresholdX || + topDiff > thresholdY || bottomDiff > thresholdY; +} + +void Alsc::switchMode(CameraMode const &cameraMode, + [[maybe_unused]] Metadata *metadata) +{ + /* + * We're going to start over with the tables if there's any "significant" + * change. + */ + bool resetTables = firstTime_ || compareModes(cameraMode_, cameraMode); + + /* Believe the colour temperature from the AWB, if there is one. */ + ct_ = getCt(metadata, ct_); + + /* Ensure the other thread isn't running while we do this. */ + waitForAysncThread(); + + cameraMode_ = cameraMode; + + /* + * We must resample the luminance table like we do the others, but it's + * fixed so we can simply do it up front here. + */ + resampleCalTable(config_.luminanceLut, cameraMode_, luminanceTable_); + + if (resetTables) { + /* + * Upon every "table reset", arrange for something sensible to be + * generated. Construct the tables for the previous recorded colour + * temperature. In order to start over from scratch we initialise + * the lambdas, but the rest of this code then echoes the code in + * doAlsc, without the adaptive algorithm. + */ + std::fill(lambdaR_.begin(), lambdaR_.end(), 1.0); + std::fill(lambdaB_.begin(), lambdaB_.end(), 1.0); + Array2D &calTableR = tmpC_[0], &calTableB = tmpC_[1], &calTableTmp = tmpC_[2]; + getCalTable(ct_, config_.calibrationsCr, calTableTmp); + resampleCalTable(calTableTmp, cameraMode_, calTableR); + getCalTable(ct_, config_.calibrationsCb, calTableTmp); + resampleCalTable(calTableTmp, cameraMode_, calTableB); + compensateLambdasForCal(calTableR, lambdaR_, asyncLambdaR_); + compensateLambdasForCal(calTableB, lambdaB_, asyncLambdaB_); + addLuminanceToTables(syncResults_, asyncLambdaR_, 1.0, asyncLambdaB_, + luminanceTable_, config_.luminanceStrength); + prevSyncResults_ = syncResults_; + framePhase_ = config_.framePeriod; /* run the algo again asap */ + firstTime_ = false; + } +} + +void Alsc::fetchAsyncResults() +{ + LOG(RPiAlsc, Debug) << "Fetch ALSC results"; + asyncFinished_ = false; + asyncStarted_ = false; + syncResults_ = asyncResults_; +} + +double getCt(Metadata *metadata, double defaultCt) +{ + AwbStatus awbStatus; + awbStatus.temperatureK = defaultCt; /* in case nothing found */ + if (metadata->get("awb.status", awbStatus) != 0) + LOG(RPiAlsc, Debug) << "no AWB results found, using " + << awbStatus.temperatureK; + else + LOG(RPiAlsc, Debug) << "AWB results found, using " + << awbStatus.temperatureK; + return awbStatus.temperatureK; +} + +static void copyStats(RgbyRegions ®ions, StatisticsPtr &stats, + AlscStatus const &status) +{ + if (!regions.numRegions()) + regions.init(stats->awbRegions.size()); + + const std::vector &rTable = status.r; + const std::vector &gTable = status.g; + const std::vector &bTable = status.b; + for (unsigned int i = 0; i < stats->awbRegions.numRegions(); i++) { + auto r = stats->awbRegions.get(i); + r.val.rSum = static_cast(r.val.rSum / rTable[i]); + r.val.gSum = static_cast(r.val.gSum / gTable[i]); + r.val.bSum = static_cast(r.val.bSum / bTable[i]); + regions.set(i, r); + } +} + +void Alsc::restartAsync(StatisticsPtr &stats, Metadata *imageMetadata) +{ + LOG(RPiAlsc, Debug) << "Starting ALSC calculation"; + /* + * Get the current colour temperature. It's all we need from the + * metadata. Default to the last CT value (which could be the default). + */ + ct_ = getCt(imageMetadata, ct_); + /* + * We have to copy the statistics here, dividing out our best guess of + * the LSC table that the pipeline applied to them. + */ + AlscStatus alscStatus; + if (imageMetadata->get("alsc.status", alscStatus) != 0) { + LOG(RPiAlsc, Warning) + << "No ALSC status found for applied gains!"; + alscStatus.r.resize(config_.tableSize.width * config_.tableSize.height, 1.0); + alscStatus.g.resize(config_.tableSize.width * config_.tableSize.height, 1.0); + alscStatus.b.resize(config_.tableSize.width * config_.tableSize.height, 1.0); + } + copyStats(statistics_, stats, alscStatus); + framePhase_ = 0; + asyncStarted_ = true; + { + std::lock_guard lock(mutex_); + asyncStart_ = true; + } + asyncSignal_.notify_one(); +} + +void Alsc::prepare(Metadata *imageMetadata) +{ + /* + * Count frames since we started, and since we last poked the async + * thread. + */ + if (frameCount_ < (int)config_.startupFrames) + frameCount_++; + double speed = frameCount_ < (int)config_.startupFrames + ? 1.0 + : config_.speed; + LOG(RPiAlsc, Debug) + << "frame count " << frameCount_ << " speed " << speed; + { + std::unique_lock lock(mutex_); + if (asyncStarted_ && asyncFinished_) + fetchAsyncResults(); + } + /* Apply IIR filter to results and program into the pipeline. */ + for (unsigned int j = 0; j < syncResults_.size(); j++) { + for (unsigned int i = 0; i < syncResults_[j].size(); i++) + prevSyncResults_[j][i] = speed * syncResults_[j][i] + (1.0 - speed) * prevSyncResults_[j][i]; + } + /* Put output values into status metadata. */ + AlscStatus status; + status.r = prevSyncResults_[0].data(); + status.g = prevSyncResults_[1].data(); + status.b = prevSyncResults_[2].data(); + imageMetadata->set("alsc.status", status); +} + +void Alsc::process(StatisticsPtr &stats, Metadata *imageMetadata) +{ + /* + * Count frames since we started, and since we last poked the async + * thread. + */ + if (framePhase_ < (int)config_.framePeriod) + framePhase_++; + if (frameCount2_ < (int)config_.startupFrames) + frameCount2_++; + LOG(RPiAlsc, Debug) << "frame_phase " << framePhase_; + if (framePhase_ >= (int)config_.framePeriod || + frameCount2_ < (int)config_.startupFrames) { + if (asyncStarted_ == false) + restartAsync(stats, imageMetadata); + } +} + +void Alsc::asyncFunc() +{ + while (true) { + { + std::unique_lock lock(mutex_); + asyncSignal_.wait(lock, [&] { + return asyncStart_ || asyncAbort_; + }); + asyncStart_ = false; + if (asyncAbort_) + break; + } + doAlsc(); + { + std::lock_guard lock(mutex_); + asyncFinished_ = true; + } + syncSignal_.notify_one(); + } +} + +void getCalTable(double ct, std::vector const &calibrations, + Array2D &calTable) +{ + if (calibrations.empty()) { + std::fill(calTable.begin(), calTable.end(), 1.0); + LOG(RPiAlsc, Debug) << "no calibrations found"; + } else if (ct <= calibrations.front().ct) { + calTable = calibrations.front().table; + LOG(RPiAlsc, Debug) << "using calibration for " + << calibrations.front().ct; + } else if (ct >= calibrations.back().ct) { + calTable = calibrations.back().table; + LOG(RPiAlsc, Debug) << "using calibration for " + << calibrations.back().ct; + } else { + int idx = 0; + while (ct > calibrations[idx + 1].ct) + idx++; + double ct0 = calibrations[idx].ct, ct1 = calibrations[idx + 1].ct; + LOG(RPiAlsc, Debug) + << "ct is " << ct << ", interpolating between " + << ct0 << " and " << ct1; + for (unsigned int i = 0; i < calTable.size(); i++) + calTable[i] = + (calibrations[idx].table[i] * (ct1 - ct) + + calibrations[idx + 1].table[i] * (ct - ct0)) / + (ct1 - ct0); + } +} + +void resampleCalTable(const Array2D &calTableIn, + CameraMode const &cameraMode, + Array2D &calTableOut) +{ + int X = calTableIn.dimensions().width; + int Y = calTableIn.dimensions().height; + + /* + * Precalculate and cache the x sampling locations and phases to save + * recomputing them on every row. + */ + int xLo[X], xHi[X]; + double xf[X]; + double scaleX = cameraMode.sensorWidth / + (cameraMode.width * cameraMode.scaleX); + double xOff = cameraMode.cropX / (double)cameraMode.sensorWidth; + double x = .5 / scaleX + xOff * X - .5; + double xInc = 1 / scaleX; + for (int i = 0; i < X; i++, x += xInc) { + xLo[i] = floor(x); + xf[i] = x - xLo[i]; + xHi[i] = std::min(xLo[i] + 1, X - 1); + xLo[i] = std::max(xLo[i], 0); + if (!!(cameraMode.transform & libcamera::Transform::HFlip)) { + xLo[i] = X - 1 - xLo[i]; + xHi[i] = X - 1 - xHi[i]; + } + } + /* Now march over the output table generating the new values. */ + double scaleY = cameraMode.sensorHeight / + (cameraMode.height * cameraMode.scaleY); + double yOff = cameraMode.cropY / (double)cameraMode.sensorHeight; + double y = .5 / scaleY + yOff * Y - .5; + double yInc = 1 / scaleY; + for (int j = 0; j < Y; j++, y += yInc) { + int yLo = floor(y); + double yf = y - yLo; + int yHi = std::min(yLo + 1, Y - 1); + yLo = std::max(yLo, 0); + if (!!(cameraMode.transform & libcamera::Transform::VFlip)) { + yLo = Y - 1 - yLo; + yHi = Y - 1 - yHi; + } + double const *rowAbove = calTableIn.ptr() + X * yLo; + double const *rowBelow = calTableIn.ptr() + X * yHi; + double *out = calTableOut.ptr() + X * j; + for (int i = 0; i < X; i++) { + double above = rowAbove[xLo[i]] * (1 - xf[i]) + + rowAbove[xHi[i]] * xf[i]; + double below = rowBelow[xLo[i]] * (1 - xf[i]) + + rowBelow[xHi[i]] * xf[i]; + *(out++) = above * (1 - yf) + below * yf; + } + } +} + +/* Calculate chrominance statistics (R/G and B/G) for each region. */ +static void calculateCrCb(const RgbyRegions &awbRegion, Array2D &cr, + Array2D &cb, uint32_t minCount, uint16_t minG) +{ + for (unsigned int i = 0; i < cr.size(); i++) { + auto s = awbRegion.get(i); + + if (s.counted <= minCount || s.val.gSum / s.counted <= minG) { + cr[i] = cb[i] = InsufficientData; + continue; + } + + cr[i] = s.val.rSum / (double)s.val.gSum; + cb[i] = s.val.bSum / (double)s.val.gSum; + } +} + +static void applyCalTable(const Array2D &calTable, Array2D &C) +{ + for (unsigned int i = 0; i < C.size(); i++) + if (C[i] != InsufficientData) + C[i] *= calTable[i]; +} + +void compensateLambdasForCal(const Array2D &calTable, + const Array2D &oldLambdas, + Array2D &newLambdas) +{ + double minNewLambda = std::numeric_limits::max(); + for (unsigned int i = 0; i < newLambdas.size(); i++) { + newLambdas[i] = oldLambdas[i] * calTable[i]; + minNewLambda = std::min(minNewLambda, newLambdas[i]); + } + for (unsigned int i = 0; i < newLambdas.size(); i++) + newLambdas[i] /= minNewLambda; +} + +[[maybe_unused]] static void printCalTable(const Array2D &C) +{ + const Size &size = C.dimensions(); + printf("table: [\n"); + for (unsigned int j = 0; j < size.height; j++) { + for (unsigned int i = 0; i < size.width; i++) { + printf("%5.3f", 1.0 / C[j * size.width + i]); + if (i != size.width - 1 || j != size.height - 1) + printf(","); + } + printf("\n"); + } + printf("]\n"); +} + +/* + * Compute weight out of 1.0 which reflects how similar we wish to make the + * colours of these two regions. + */ +static double computeWeight(double Ci, double Cj, double sigma) +{ + if (Ci == InsufficientData || Cj == InsufficientData) + return 0; + double diff = (Ci - Cj) / sigma; + return exp(-diff * diff / 2); +} + +/* Compute all weights. */ +static void computeW(const Array2D &C, double sigma, + SparseArray &W) +{ + size_t XY = C.size(); + size_t X = C.dimensions().width; + + for (unsigned int i = 0; i < XY; i++) { + /* Start with neighbour above and go clockwise. */ + W[i][0] = i >= X ? computeWeight(C[i], C[i - X], sigma) : 0; + W[i][1] = i % X < X - 1 ? computeWeight(C[i], C[i + 1], sigma) : 0; + W[i][2] = i < XY - X ? computeWeight(C[i], C[i + X], sigma) : 0; + W[i][3] = i % X ? computeWeight(C[i], C[i - 1], sigma) : 0; + } +} + +/* Compute M, the large but sparse matrix such that M * lambdas = 0. */ +static void constructM(const Array2D &C, + const SparseArray &W, + SparseArray &M) +{ + size_t XY = C.size(); + size_t X = C.dimensions().width; + + double epsilon = 0.001; + for (unsigned int i = 0; i < XY; i++) { + /* + * Note how, if C[i] == INSUFFICIENT_DATA, the weights will all + * be zero so the equation is still set up correctly. + */ + int m = !!(i >= X) + !!(i % X < X - 1) + !!(i < XY - X) + + !!(i % X); /* total number of neighbours */ + /* we'll divide the diagonal out straight away */ + double diagonal = (epsilon + W[i][0] + W[i][1] + W[i][2] + W[i][3]) * C[i]; + M[i][0] = i >= X ? (W[i][0] * C[i - X] + epsilon / m * C[i]) / diagonal : 0; + M[i][1] = i % X < X - 1 ? (W[i][1] * C[i + 1] + epsilon / m * C[i]) / diagonal : 0; + M[i][2] = i < XY - X ? (W[i][2] * C[i + X] + epsilon / m * C[i]) / diagonal : 0; + M[i][3] = i % X ? (W[i][3] * C[i - 1] + epsilon / m * C[i]) / diagonal : 0; + } +} + +/* + * In the compute_lambda_ functions, note that the matrix coefficients for the + * left/right neighbours are zero down the left/right edges, so we don't need + * need to test the i value to exclude them. + */ +static double computeLambdaBottom(int i, const SparseArray &M, + Array2D &lambda) +{ + return M[i][1] * lambda[i + 1] + M[i][2] * lambda[i + lambda.dimensions().width] + + M[i][3] * lambda[i - 1]; +} +static double computeLambdaBottomStart(int i, const SparseArray &M, + Array2D &lambda) +{ + return M[i][1] * lambda[i + 1] + M[i][2] * lambda[i + lambda.dimensions().width]; +} +static double computeLambdaInterior(int i, const SparseArray &M, + Array2D &lambda) +{ + return M[i][0] * lambda[i - lambda.dimensions().width] + M[i][1] * lambda[i + 1] + + M[i][2] * lambda[i + lambda.dimensions().width] + M[i][3] * lambda[i - 1]; +} +static double computeLambdaTop(int i, const SparseArray &M, + Array2D &lambda) +{ + return M[i][0] * lambda[i - lambda.dimensions().width] + M[i][1] * lambda[i + 1] + + M[i][3] * lambda[i - 1]; +} +static double computeLambdaTopEnd(int i, const SparseArray &M, + Array2D &lambda) +{ + return M[i][0] * lambda[i - lambda.dimensions().width] + M[i][3] * lambda[i - 1]; +} + +/* Gauss-Seidel iteration with over-relaxation. */ +static double gaussSeidel2Sor(const SparseArray &M, double omega, + Array2D &lambda, double lambdaBound) +{ + int XY = lambda.size(); + int X = lambda.dimensions().width; + const double min = 1 - lambdaBound, max = 1 + lambdaBound; + Array2D oldLambda = lambda; + int i; + lambda[0] = computeLambdaBottomStart(0, M, lambda); + lambda[0] = std::clamp(lambda[0], min, max); + for (i = 1; i < X; i++) { + lambda[i] = computeLambdaBottom(i, M, lambda); + lambda[i] = std::clamp(lambda[i], min, max); + } + for (; i < XY - X; i++) { + lambda[i] = computeLambdaInterior(i, M, lambda); + lambda[i] = std::clamp(lambda[i], min, max); + } + for (; i < XY - 1; i++) { + lambda[i] = computeLambdaTop(i, M, lambda); + lambda[i] = std::clamp(lambda[i], min, max); + } + lambda[i] = computeLambdaTopEnd(i, M, lambda); + lambda[i] = std::clamp(lambda[i], min, max); + /* + * Also solve the system from bottom to top, to help spread the updates + * better. + */ + lambda[i] = computeLambdaTopEnd(i, M, lambda); + lambda[i] = std::clamp(lambda[i], min, max); + for (i = XY - 2; i >= XY - X; i--) { + lambda[i] = computeLambdaTop(i, M, lambda); + lambda[i] = std::clamp(lambda[i], min, max); + } + for (; i >= X; i--) { + lambda[i] = computeLambdaInterior(i, M, lambda); + lambda[i] = std::clamp(lambda[i], min, max); + } + for (; i >= 1; i--) { + lambda[i] = computeLambdaBottom(i, M, lambda); + lambda[i] = std::clamp(lambda[i], min, max); + } + lambda[0] = computeLambdaBottomStart(0, M, lambda); + lambda[0] = std::clamp(lambda[0], min, max); + double maxDiff = 0; + for (i = 0; i < XY; i++) { + lambda[i] = oldLambda[i] + (lambda[i] - oldLambda[i]) * omega; + if (fabs(lambda[i] - oldLambda[i]) > fabs(maxDiff)) + maxDiff = lambda[i] - oldLambda[i]; + } + return maxDiff; +} + +/* Normalise the values so that the smallest value is 1. */ +static void normalise(Array2D &results) +{ + double minval = *std::min_element(results.begin(), results.end()); + std::for_each(results.begin(), results.end(), + [minval](double val) { return val / minval; }); +} + +/* Rescale the values so that the average value is 1. */ +static void reaverage(Array2D &data) +{ + double sum = std::accumulate(data.begin(), data.end(), 0.0); + double ratio = 1 / (sum / data.size()); + std::for_each(data.begin(), data.end(), + [ratio](double val) { return val * ratio; }); +} + +static void runMatrixIterations(const Array2D &C, + Array2D &lambda, + const SparseArray &W, + SparseArray &M, double omega, + unsigned int nIter, double threshold, double lambdaBound) +{ + constructM(C, W, M); + double lastMaxDiff = std::numeric_limits::max(); + for (unsigned int i = 0; i < nIter; i++) { + double maxDiff = fabs(gaussSeidel2Sor(M, omega, lambda, lambdaBound)); + if (maxDiff < threshold) { + LOG(RPiAlsc, Debug) + << "Stop after " << i + 1 << " iterations"; + break; + } + /* + * this happens very occasionally (so make a note), though + * doesn't seem to matter + */ + if (maxDiff > lastMaxDiff) + LOG(RPiAlsc, Debug) + << "Iteration " << i << ": maxDiff gone up " + << lastMaxDiff << " to " << maxDiff; + lastMaxDiff = maxDiff; + } + /* We're going to normalise the lambdas so the total average is 1. */ + reaverage(lambda); +} + +static void addLuminanceRb(Array2D &result, const Array2D &lambda, + const Array2D &luminanceLut, + double luminanceStrength) +{ + for (unsigned int i = 0; i < result.size(); i++) + result[i] = lambda[i] * ((luminanceLut[i] - 1) * luminanceStrength + 1); +} + +static void addLuminanceG(Array2D &result, double lambda, + const Array2D &luminanceLut, + double luminanceStrength) +{ + for (unsigned int i = 0; i < result.size(); i++) + result[i] = lambda * ((luminanceLut[i] - 1) * luminanceStrength + 1); +} + +void addLuminanceToTables(std::array, 3> &results, + const Array2D &lambdaR, + double lambdaG, const Array2D &lambdaB, + const Array2D &luminanceLut, + double luminanceStrength) +{ + addLuminanceRb(results[0], lambdaR, luminanceLut, luminanceStrength); + addLuminanceG(results[1], lambdaG, luminanceLut, luminanceStrength); + addLuminanceRb(results[2], lambdaB, luminanceLut, luminanceStrength); + for (auto &r : results) + normalise(r); +} + +void Alsc::doAlsc() +{ + Array2D &cr = tmpC_[0], &cb = tmpC_[1], &calTableR = tmpC_[2], + &calTableB = tmpC_[3], &calTableTmp = tmpC_[4]; + SparseArray &wr = tmpM_[0], &wb = tmpM_[1], &M = tmpM_[2]; + + /* + * Calculate our R/B ("Cr"/"Cb") colour statistics, and assess which are + * usable. + */ + calculateCrCb(statistics_, cr, cb, config_.minCount, config_.minG); + /* + * Fetch the new calibrations (if any) for this CT. Resample them in + * case the camera mode is not full-frame. + */ + getCalTable(ct_, config_.calibrationsCr, calTableTmp); + resampleCalTable(calTableTmp, cameraMode_, calTableR); + getCalTable(ct_, config_.calibrationsCb, calTableTmp); + resampleCalTable(calTableTmp, cameraMode_, calTableB); + /* + * You could print out the cal tables for this image here, if you're + * tuning the algorithm... + * Apply any calibration to the statistics, so the adaptive algorithm + * makes only the extra adjustments. + */ + applyCalTable(calTableR, cr); + applyCalTable(calTableB, cb); + /* Compute weights between zones. */ + computeW(cr, config_.sigmaCr, wr); + computeW(cb, config_.sigmaCb, wb); + /* Run Gauss-Seidel iterations over the resulting matrix, for R and B. */ + runMatrixIterations(cr, lambdaR_, wr, M, config_.omega, config_.nIter, + config_.threshold, config_.lambdaBound); + runMatrixIterations(cb, lambdaB_, wb, M, config_.omega, config_.nIter, + config_.threshold, config_.lambdaBound); + /* + * Fold the calibrated gains into our final lambda values. (Note that on + * the next run, we re-start with the lambda values that don't have the + * calibration gains included.) + */ + compensateLambdasForCal(calTableR, lambdaR_, asyncLambdaR_); + compensateLambdasForCal(calTableB, lambdaB_, asyncLambdaB_); + /* Fold in the luminance table at the appropriate strength. */ + addLuminanceToTables(asyncResults_, asyncLambdaR_, 1.0, + asyncLambdaB_, luminanceTable_, + config_.luminanceStrength); +} + +/* Register algorithm with the system. */ +static Algorithm *create(Controller *controller) +{ + return (Algorithm *)new Alsc(controller); +} +static RegisterAlgorithm reg(NAME, &create); diff --git a/src/ipa/rpi/controller/rpi/alsc.h b/src/ipa/rpi/controller/rpi/alsc.h new file mode 100644 index 00000000..0b6d9478 --- /dev/null +++ b/src/ipa/rpi/controller/rpi/alsc.h @@ -0,0 +1,174 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (C) 2019, Raspberry Pi Ltd + * + * alsc.h - ALSC (auto lens shading correction) control algorithm + */ +#pragma once + +#include +#include +#include +#include +#include + +#include + +#include "../algorithm.h" +#include "../alsc_status.h" +#include "../statistics.h" + +namespace RPiController { + +/* Algorithm to generate automagic LSC (Lens Shading Correction) tables. */ + +/* + * The Array2D class is a very thin wrapper round std::vector so that it can + * be used in exactly the same way in the code but carries its correct width + * and height ("dimensions") with it. + */ + +template +class Array2D +{ +public: + using Size = libcamera::Size; + + const Size &dimensions() const { return dimensions_; } + + size_t size() const { return data_.size(); } + + const std::vector &data() const { return data_; } + + void resize(const Size &dims) + { + dimensions_ = dims; + data_.resize(dims.width * dims.height); + } + + void resize(const Size &dims, const T &value) + { + resize(dims); + std::fill(data_.begin(), data_.end(), value); + } + + T &operator[](int index) { return data_[index]; } + + const T &operator[](int index) const { return data_[index]; } + + T *ptr() { return data_.data(); } + + const T *ptr() const { return data_.data(); } + + auto begin() { return data_.begin(); } + auto end() { return data_.end(); } + +private: + Size dimensions_; + std::vector data_; +}; + +/* + * We'll use the term SparseArray for the large sparse matrices that are + * XY tall but have only 4 non-zero elements on each row. + */ + +template +using SparseArray = std::vector>; + +struct AlscCalibration { + double ct; + Array2D table; +}; + +struct AlscConfig { + /* Only repeat the ALSC calculation every "this many" frames */ + uint16_t framePeriod; + /* number of initial frames for which speed taken as 1.0 (maximum) */ + uint16_t startupFrames; + /* IIR filter speed applied to algorithm results */ + double speed; + double sigmaCr; + double sigmaCb; + double minCount; + uint16_t minG; + double omega; + uint32_t nIter; + Array2D luminanceLut; + double luminanceStrength; + std::vector calibrationsCr; + std::vector calibrationsCb; + double defaultCt; /* colour temperature if no metadata found */ + double threshold; /* iteration termination threshold */ + double lambdaBound; /* upper/lower bound for lambda from a value of 1 */ + libcamera::Size tableSize; +}; + +class Alsc : public Algorithm +{ +public: + Alsc(Controller *controller = NULL); + ~Alsc(); + char const *name() const override; + void initialise() override; + void switchMode(CameraMode const &cameraMode, Metadata *metadata) override; + int read(const libcamera::YamlObject ¶ms) override; + void prepare(Metadata *imageMetadata) override; + void process(StatisticsPtr &stats, Metadata *imageMetadata) override; + +private: + /* configuration is read-only, and available to both threads */ + AlscConfig config_; + bool firstTime_; + CameraMode cameraMode_; + Array2D luminanceTable_; + std::thread asyncThread_; + void asyncFunc(); /* asynchronous thread function */ + std::mutex mutex_; + /* condvar for async thread to wait on */ + std::condition_variable asyncSignal_; + /* condvar for synchronous thread to wait on */ + std::condition_variable syncSignal_; + /* for sync thread to check if async thread finished (requires mutex) */ + bool asyncFinished_; + /* for async thread to check if it's been told to run (requires mutex) */ + bool asyncStart_; + /* for async thread to check if it's been told to quit (requires mutex) */ + bool asyncAbort_; + + /* + * The following are only for the synchronous thread to use: + * for sync thread to note its has asked async thread to run + */ + bool asyncStarted_; + /* counts up to framePeriod before restarting the async thread */ + int framePhase_; + /* counts up to startupFrames */ + int frameCount_; + /* counts up to startupFrames for Process function */ + int frameCount2_; + std::array, 3> syncResults_; + std::array, 3> prevSyncResults_; + void waitForAysncThread(); + /* + * The following are for the asynchronous thread to use, though the main + * thread can set/reset them if the async thread is known to be idle: + */ + void restartAsync(StatisticsPtr &stats, Metadata *imageMetadata); + /* copy out the results from the async thread so that it can be restarted */ + void fetchAsyncResults(); + double ct_; + RgbyRegions statistics_; + std::array, 3> asyncResults_; + Array2D asyncLambdaR_; + Array2D asyncLambdaB_; + void doAlsc(); + Array2D lambdaR_; + Array2D lambdaB_; + + /* Temporaries for the computations */ + std::array, 5> tmpC_; + std::array, 3> tmpM_; +}; + +} /* namespace RPiController */ diff --git a/src/ipa/rpi/controller/rpi/awb.cpp b/src/ipa/rpi/controller/rpi/awb.cpp new file mode 100644 index 00000000..ef3435d6 --- /dev/null +++ b/src/ipa/rpi/controller/rpi/awb.cpp @@ -0,0 +1,734 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (C) 2019, Raspberry Pi Ltd + * + * awb.cpp - AWB control algorithm + */ + +#include +#include + +#include + +#include "../lux_status.h" + +#include "awb.h" + +using namespace RPiController; +using namespace libcamera; + +LOG_DEFINE_CATEGORY(RPiAwb) + +#define NAME "rpi.awb" + +/* + * todo - the locking in this algorithm needs some tidying up as has been done + * elsewhere (ALSC and AGC). + */ + +int AwbMode::read(const libcamera::YamlObject ¶ms) +{ + auto value = params["lo"].get(); + if (!value) + return -EINVAL; + ctLo = *value; + + value = params["hi"].get(); + if (!value) + return -EINVAL; + ctHi = *value; + + return 0; +} + +int AwbPrior::read(const libcamera::YamlObject ¶ms) +{ + auto value = params["lux"].get(); + if (!value) + return -EINVAL; + lux = *value; + + return prior.read(params["prior"]); +} + +static int readCtCurve(Pwl &ctR, Pwl &ctB, const libcamera::YamlObject ¶ms) +{ + if (params.size() % 3) { + LOG(RPiAwb, Error) << "AwbConfig: incomplete CT curve entry"; + return -EINVAL; + } + + if (params.size() < 6) { + LOG(RPiAwb, Error) << "AwbConfig: insufficient points in CT curve"; + return -EINVAL; + } + + const auto &list = params.asList(); + + for (auto it = list.begin(); it != list.end(); it++) { + auto value = it->get(); + if (!value) + return -EINVAL; + double ct = *value; + + assert(it == list.begin() || ct != ctR.domain().end); + + value = (++it)->get(); + if (!value) + return -EINVAL; + ctR.append(ct, *value); + + value = (++it)->get(); + if (!value) + return -EINVAL; + ctB.append(ct, *value); + } + + return 0; +} + +int AwbConfig::read(const libcamera::YamlObject ¶ms) +{ + int ret; + + bayes = params["bayes"].get(1); + framePeriod = params["frame_period"].get(10); + startupFrames = params["startup_frames"].get(10); + convergenceFrames = params["convergence_frames"].get(3); + speed = params["speed"].get(0.05); + + if (params.contains("ct_curve")) { + ret = readCtCurve(ctR, ctB, params["ct_curve"]); + if (ret) + return ret; + /* We will want the inverse functions of these too. */ + ctRInverse = ctR.inverse(); + ctBInverse = ctB.inverse(); + } + + if (params.contains("priors")) { + for (const auto &p : params["priors"].asList()) { + AwbPrior prior; + ret = prior.read(p); + if (ret) + return ret; + if (!priors.empty() && prior.lux <= priors.back().lux) { + LOG(RPiAwb, Error) << "AwbConfig: Prior must be ordered in increasing lux value"; + return -EINVAL; + } + priors.push_back(prior); + } + if (priors.empty()) { + LOG(RPiAwb, Error) << "AwbConfig: no AWB priors configured"; + return ret; + } + } + if (params.contains("modes")) { + for (const auto &[key, value] : params["modes"].asDict()) { + ret = modes[key].read(value); + if (ret) + return ret; + if (defaultMode == nullptr) + defaultMode = &modes[key]; + } + if (defaultMode == nullptr) { + LOG(RPiAwb, Error) << "AwbConfig: no AWB modes configured"; + return -EINVAL; + } + } + + minPixels = params["min_pixels"].get(16.0); + minG = params["min_G"].get(32); + minRegions = params["min_regions"].get(10); + deltaLimit = params["delta_limit"].get(0.2); + coarseStep = params["coarse_step"].get(0.2); + transversePos = params["transverse_pos"].get(0.01); + transverseNeg = params["transverse_neg"].get(0.01); + if (transversePos <= 0 || transverseNeg <= 0) { + LOG(RPiAwb, Error) << "AwbConfig: transverse_pos/neg must be > 0"; + return -EINVAL; + } + + sensitivityR = params["sensitivity_r"].get(1.0); + sensitivityB = params["sensitivity_b"].get(1.0); + + if (bayes) { + if (ctR.empty() || ctB.empty() || priors.empty() || + defaultMode == nullptr) { + LOG(RPiAwb, Warning) + << "Bayesian AWB mis-configured - switch to Grey method"; + bayes = false; + } + } + fast = params[fast].get(bayes); /* default to fast for Bayesian, otherwise slow */ + whitepointR = params["whitepoint_r"].get(0.0); + whitepointB = params["whitepoint_b"].get(0.0); + if (bayes == false) + sensitivityR = sensitivityB = 1.0; /* nor do sensitivities make any sense */ + return 0; +} + +Awb::Awb(Controller *controller) + : AwbAlgorithm(controller) +{ + asyncAbort_ = asyncStart_ = asyncStarted_ = asyncFinished_ = false; + mode_ = nullptr; + manualR_ = manualB_ = 0.0; + asyncThread_ = std::thread(std::bind(&Awb::asyncFunc, this)); +} + +Awb::~Awb() +{ + { + std::lock_guard lock(mutex_); + asyncAbort_ = true; + } + asyncSignal_.notify_one(); + asyncThread_.join(); +} + +char const *Awb::name() const +{ + return NAME; +} + +int Awb::read(const libcamera::YamlObject ¶ms) +{ + return config_.read(params); +} + +void Awb::initialise() +{ + frameCount_ = framePhase_ = 0; + /* + * Put something sane into the status that we are filtering towards, + * just in case the first few frames don't have anything meaningful in + * them. + */ + if (!config_.ctR.empty() && !config_.ctB.empty()) { + syncResults_.temperatureK = config_.ctR.domain().clip(4000); + syncResults_.gainR = 1.0 / config_.ctR.eval(syncResults_.temperatureK); + syncResults_.gainG = 1.0; + syncResults_.gainB = 1.0 / config_.ctB.eval(syncResults_.temperatureK); + } else { + /* random values just to stop the world blowing up */ + syncResults_.temperatureK = 4500; + syncResults_.gainR = syncResults_.gainG = syncResults_.gainB = 1.0; + } + prevSyncResults_ = syncResults_; + asyncResults_ = syncResults_; +} + +void Awb::disableAuto() +{ + /* Freeze the most recent values, and treat them as manual gains */ + manualR_ = syncResults_.gainR = prevSyncResults_.gainR; + manualB_ = syncResults_.gainB = prevSyncResults_.gainB; + syncResults_.gainG = prevSyncResults_.gainG; + syncResults_.temperatureK = prevSyncResults_.temperatureK; +} + +void Awb::enableAuto() +{ + manualR_ = 0.0; + manualB_ = 0.0; +} + +unsigned int Awb::getConvergenceFrames() const +{ + /* + * If not in auto mode, there is no convergence + * to happen, so no need to drop any frames - return zero. + */ + if (!isAutoEnabled()) + return 0; + else + return config_.convergenceFrames; +} + +void Awb::setMode(std::string const &modeName) +{ + modeName_ = modeName; +} + +void Awb::setManualGains(double manualR, double manualB) +{ + /* If any of these are 0.0, we swich back to auto. */ + manualR_ = manualR; + manualB_ = manualB; + /* + * If not in auto mode, set these values into the syncResults which + * means that Prepare() will adopt them immediately. + */ + if (!isAutoEnabled()) { + syncResults_.gainR = prevSyncResults_.gainR = manualR_; + syncResults_.gainG = prevSyncResults_.gainG = 1.0; + syncResults_.gainB = prevSyncResults_.gainB = manualB_; + if (config_.bayes) { + /* Also estimate the best corresponding colour temperature from the curves. */ + double ctR = config_.ctRInverse.eval(config_.ctRInverse.domain().clip(1 / manualR_)); + double ctB = config_.ctBInverse.eval(config_.ctBInverse.domain().clip(1 / manualB_)); + prevSyncResults_.temperatureK = (ctR + ctB) / 2; + syncResults_.temperatureK = prevSyncResults_.temperatureK; + } + } +} + +void Awb::switchMode([[maybe_unused]] CameraMode const &cameraMode, + Metadata *metadata) +{ + /* Let other algorithms know the current white balance values. */ + metadata->set("awb.status", prevSyncResults_); +} + +bool Awb::isAutoEnabled() const +{ + return manualR_ == 0.0 || manualB_ == 0.0; +} + +void Awb::fetchAsyncResults() +{ + LOG(RPiAwb, Debug) << "Fetch AWB results"; + asyncFinished_ = false; + asyncStarted_ = false; + /* + * It's possible manual gains could be set even while the async + * thread was running, so only copy the results if still in auto mode. + */ + if (isAutoEnabled()) + syncResults_ = asyncResults_; +} + +void Awb::restartAsync(StatisticsPtr &stats, double lux) +{ + LOG(RPiAwb, Debug) << "Starting AWB calculation"; + /* this makes a new reference which belongs to the asynchronous thread */ + statistics_ = stats; + /* store the mode as it could technically change */ + auto m = config_.modes.find(modeName_); + mode_ = m != config_.modes.end() + ? &m->second + : (mode_ == nullptr ? config_.defaultMode : mode_); + lux_ = lux; + framePhase_ = 0; + asyncStarted_ = true; + size_t len = modeName_.copy(asyncResults_.mode, + sizeof(asyncResults_.mode) - 1); + asyncResults_.mode[len] = '\0'; + { + std::lock_guard lock(mutex_); + asyncStart_ = true; + } + asyncSignal_.notify_one(); +} + +void Awb::prepare(Metadata *imageMetadata) +{ + if (frameCount_ < (int)config_.startupFrames) + frameCount_++; + double speed = frameCount_ < (int)config_.startupFrames + ? 1.0 + : config_.speed; + LOG(RPiAwb, Debug) + << "frame_count " << frameCount_ << " speed " << speed; + { + std::unique_lock lock(mutex_); + if (asyncStarted_ && asyncFinished_) + fetchAsyncResults(); + } + /* Finally apply IIR filter to results and put into metadata. */ + memcpy(prevSyncResults_.mode, syncResults_.mode, + sizeof(prevSyncResults_.mode)); + prevSyncResults_.temperatureK = speed * syncResults_.temperatureK + + (1.0 - speed) * prevSyncResults_.temperatureK; + prevSyncResults_.gainR = speed * syncResults_.gainR + + (1.0 - speed) * prevSyncResults_.gainR; + prevSyncResults_.gainG = speed * syncResults_.gainG + + (1.0 - speed) * prevSyncResults_.gainG; + prevSyncResults_.gainB = speed * syncResults_.gainB + + (1.0 - speed) * prevSyncResults_.gainB; + imageMetadata->set("awb.status", prevSyncResults_); + LOG(RPiAwb, Debug) + << "Using AWB gains r " << prevSyncResults_.gainR << " g " + << prevSyncResults_.gainG << " b " + << prevSyncResults_.gainB; +} + +void Awb::process(StatisticsPtr &stats, Metadata *imageMetadata) +{ + /* Count frames since we last poked the async thread. */ + if (framePhase_ < (int)config_.framePeriod) + framePhase_++; + LOG(RPiAwb, Debug) << "frame_phase " << framePhase_; + /* We do not restart the async thread if we're not in auto mode. */ + if (isAutoEnabled() && + (framePhase_ >= (int)config_.framePeriod || + frameCount_ < (int)config_.startupFrames)) { + /* Update any settings and any image metadata that we need. */ + struct LuxStatus luxStatus = {}; + luxStatus.lux = 400; /* in case no metadata */ + if (imageMetadata->get("lux.status", luxStatus) != 0) + LOG(RPiAwb, Debug) << "No lux metadata found"; + LOG(RPiAwb, Debug) << "Awb lux value is " << luxStatus.lux; + + if (asyncStarted_ == false) + restartAsync(stats, luxStatus.lux); + } +} + +void Awb::asyncFunc() +{ + while (true) { + { + std::unique_lock lock(mutex_); + asyncSignal_.wait(lock, [&] { + return asyncStart_ || asyncAbort_; + }); + asyncStart_ = false; + if (asyncAbort_) + break; + } + doAwb(); + { + std::lock_guard lock(mutex_); + asyncFinished_ = true; + } + syncSignal_.notify_one(); + } +} + +static void generateStats(std::vector &zones, + RgbyRegions &stats, double minPixels, + double minG) +{ + for (auto const ®ion : stats) { + Awb::RGB zone; + if (region.counted >= minPixels) { + zone.G = region.val.gSum / region.counted; + if (zone.G >= minG) { + zone.R = region.val.rSum / region.counted; + zone.B = region.val.bSum / region.counted; + zones.push_back(zone); + } + } + } +} + +void Awb::prepareStats() +{ + zones_.clear(); + /* + * LSC has already been applied to the stats in this pipeline, so stop + * any LSC compensation. We also ignore config_.fast in this version. + */ + generateStats(zones_, statistics_->awbRegions, config_.minPixels, + config_.minG); + /* + * apply sensitivities, so values appear to come from our "canonical" + * sensor. + */ + for (auto &zone : zones_) { + zone.R *= config_.sensitivityR; + zone.B *= config_.sensitivityB; + } +} + +double Awb::computeDelta2Sum(double gainR, double gainB) +{ + /* + * Compute the sum of the squared colour error (non-greyness) as it + * appears in the log likelihood equation. + */ + double delta2Sum = 0; + for (auto &z : zones_) { + double deltaR = gainR * z.R - 1 - config_.whitepointR; + double deltaB = gainB * z.B - 1 - config_.whitepointB; + double delta2 = deltaR * deltaR + deltaB * deltaB; + /* LOG(RPiAwb, Debug) << "deltaR " << deltaR << " deltaB " << deltaB << " delta2 " << delta2; */ + delta2 = std::min(delta2, config_.deltaLimit); + delta2Sum += delta2; + } + return delta2Sum; +} + +Pwl Awb::interpolatePrior() +{ + /* + * Interpolate the prior log likelihood function for our current lux + * value. + */ + if (lux_ <= config_.priors.front().lux) + return config_.priors.front().prior; + else if (lux_ >= config_.priors.back().lux) + return config_.priors.back().prior; + else { + int idx = 0; + /* find which two we lie between */ + while (config_.priors[idx + 1].lux < lux_) + idx++; + double lux0 = config_.priors[idx].lux, + lux1 = config_.priors[idx + 1].lux; + return Pwl::combine(config_.priors[idx].prior, + config_.priors[idx + 1].prior, + [&](double /*x*/, double y0, double y1) { + return y0 + (y1 - y0) * + (lux_ - lux0) / (lux1 - lux0); + }); + } +} + +static double interpolateQuadatric(Pwl::Point const &a, Pwl::Point const &b, + Pwl::Point const &c) +{ + /* + * Given 3 points on a curve, find the extremum of the function in that + * interval by fitting a quadratic. + */ + const double eps = 1e-3; + Pwl::Point ca = c - a, ba = b - a; + double denominator = 2 * (ba.y * ca.x - ca.y * ba.x); + if (abs(denominator) > eps) { + double numerator = ba.y * ca.x * ca.x - ca.y * ba.x * ba.x; + double result = numerator / denominator + a.x; + return std::max(a.x, std::min(c.x, result)); + } + /* has degenerated to straight line segment */ + return a.y < c.y - eps ? a.x : (c.y < a.y - eps ? c.x : b.x); +} + +double Awb::coarseSearch(Pwl const &prior) +{ + points_.clear(); /* assume doesn't deallocate memory */ + size_t bestPoint = 0; + double t = mode_->ctLo; + int spanR = 0, spanB = 0; + /* Step down the CT curve evaluating log likelihood. */ + while (true) { + double r = config_.ctR.eval(t, &spanR); + double b = config_.ctB.eval(t, &spanB); + double gainR = 1 / r, gainB = 1 / b; + double delta2Sum = computeDelta2Sum(gainR, gainB); + double priorLogLikelihood = prior.eval(prior.domain().clip(t)); + double finalLogLikelihood = delta2Sum - priorLogLikelihood; + LOG(RPiAwb, Debug) + << "t: " << t << " gain R " << gainR << " gain B " + << gainB << " delta2_sum " << delta2Sum + << " prior " << priorLogLikelihood << " final " + << finalLogLikelihood; + points_.push_back(Pwl::Point(t, finalLogLikelihood)); + if (points_.back().y < points_[bestPoint].y) + bestPoint = points_.size() - 1; + if (t == mode_->ctHi) + break; + /* for even steps along the r/b curve scale them by the current t */ + t = std::min(t + t / 10 * config_.coarseStep, mode_->ctHi); + } + t = points_[bestPoint].x; + LOG(RPiAwb, Debug) << "Coarse search found CT " << t; + /* + * We have the best point of the search, but refine it with a quadratic + * interpolation around its neighbours. + */ + if (points_.size() > 2) { + unsigned long bp = std::min(bestPoint, points_.size() - 2); + bestPoint = std::max(1UL, bp); + t = interpolateQuadatric(points_[bestPoint - 1], + points_[bestPoint], + points_[bestPoint + 1]); + LOG(RPiAwb, Debug) + << "After quadratic refinement, coarse search has CT " + << t; + } + return t; +} + +void Awb::fineSearch(double &t, double &r, double &b, Pwl const &prior) +{ + int spanR = -1, spanB = -1; + config_.ctR.eval(t, &spanR); + config_.ctB.eval(t, &spanB); + double step = t / 10 * config_.coarseStep * 0.1; + int nsteps = 5; + double rDiff = config_.ctR.eval(t + nsteps * step, &spanR) - + config_.ctR.eval(t - nsteps * step, &spanR); + double bDiff = config_.ctB.eval(t + nsteps * step, &spanB) - + config_.ctB.eval(t - nsteps * step, &spanB); + Pwl::Point transverse(bDiff, -rDiff); + if (transverse.len2() < 1e-6) + return; + /* + * unit vector orthogonal to the b vs. r function (pointing outwards + * with r and b increasing) + */ + transverse = transverse / transverse.len(); + double bestLogLikelihood = 0, bestT = 0, bestR = 0, bestB = 0; + double transverseRange = config_.transverseNeg + config_.transversePos; + const int maxNumDeltas = 12; + /* a transverse step approximately every 0.01 r/b units */ + int numDeltas = floor(transverseRange * 100 + 0.5) + 1; + numDeltas = numDeltas < 3 ? 3 : (numDeltas > maxNumDeltas ? maxNumDeltas : numDeltas); + /* + * Step down CT curve. March a bit further if the transverse range is + * large. + */ + nsteps += numDeltas; + for (int i = -nsteps; i <= nsteps; i++) { + double tTest = t + i * step; + double priorLogLikelihood = + prior.eval(prior.domain().clip(tTest)); + double rCurve = config_.ctR.eval(tTest, &spanR); + double bCurve = config_.ctB.eval(tTest, &spanB); + /* x will be distance off the curve, y the log likelihood there */ + Pwl::Point points[maxNumDeltas]; + int bestPoint = 0; + /* Take some measurements transversely *off* the CT curve. */ + for (int j = 0; j < numDeltas; j++) { + points[j].x = -config_.transverseNeg + + (transverseRange * j) / (numDeltas - 1); + Pwl::Point rbTest = Pwl::Point(rCurve, bCurve) + + transverse * points[j].x; + double rTest = rbTest.x, bTest = rbTest.y; + double gainR = 1 / rTest, gainB = 1 / bTest; + double delta2Sum = computeDelta2Sum(gainR, gainB); + points[j].y = delta2Sum - priorLogLikelihood; + LOG(RPiAwb, Debug) + << "At t " << tTest << " r " << rTest << " b " + << bTest << ": " << points[j].y; + if (points[j].y < points[bestPoint].y) + bestPoint = j; + } + /* + * We have NUM_DELTAS points transversely across the CT curve, + * now let's do a quadratic interpolation for the best result. + */ + bestPoint = std::max(1, std::min(bestPoint, numDeltas - 2)); + Pwl::Point rbTest = Pwl::Point(rCurve, bCurve) + + transverse * interpolateQuadatric(points[bestPoint - 1], + points[bestPoint], + points[bestPoint + 1]); + double rTest = rbTest.x, bTest = rbTest.y; + double gainR = 1 / rTest, gainB = 1 / bTest; + double delta2Sum = computeDelta2Sum(gainR, gainB); + double finalLogLikelihood = delta2Sum - priorLogLikelihood; + LOG(RPiAwb, Debug) + << "Finally " + << tTest << " r " << rTest << " b " << bTest << ": " + << finalLogLikelihood + << (finalLogLikelihood < bestLogLikelihood ? " BEST" : ""); + if (bestT == 0 || finalLogLikelihood < bestLogLikelihood) + bestLogLikelihood = finalLogLikelihood, + bestT = tTest, bestR = rTest, bestB = bTest; + } + t = bestT, r = bestR, b = bestB; + LOG(RPiAwb, Debug) + << "Fine search found t " << t << " r " << r << " b " << b; +} + +void Awb::awbBayes() +{ + /* + * May as well divide out G to save computeDelta2Sum from doing it over + * and over. + */ + for (auto &z : zones_) + z.R = z.R / (z.G + 1), z.B = z.B / (z.G + 1); + /* + * Get the current prior, and scale according to how many zones are + * valid... not entirely sure about this. + */ + Pwl prior = interpolatePrior(); + prior *= zones_.size() / (double)(statistics_->awbRegions.numRegions()); + prior.map([](double x, double y) { + LOG(RPiAwb, Debug) << "(" << x << "," << y << ")"; + }); + double t = coarseSearch(prior); + double r = config_.ctR.eval(t); + double b = config_.ctB.eval(t); + LOG(RPiAwb, Debug) + << "After coarse search: r " << r << " b " << b << " (gains r " + << 1 / r << " b " << 1 / b << ")"; + /* + * Not entirely sure how to handle the fine search yet. Mostly the + * estimated CT is already good enough, but the fine search allows us to + * wander transverely off the CT curve. Under some illuminants, where + * there may be more or less green light, this may prove beneficial, + * though I probably need more real datasets before deciding exactly how + * this should be controlled and tuned. + */ + fineSearch(t, r, b, prior); + LOG(RPiAwb, Debug) + << "After fine search: r " << r << " b " << b << " (gains r " + << 1 / r << " b " << 1 / b << ")"; + /* + * Write results out for the main thread to pick up. Remember to adjust + * the gains from the ones that the "canonical sensor" would require to + * the ones needed by *this* sensor. + */ + asyncResults_.temperatureK = t; + asyncResults_.gainR = 1.0 / r * config_.sensitivityR; + asyncResults_.gainG = 1.0; + asyncResults_.gainB = 1.0 / b * config_.sensitivityB; +} + +void Awb::awbGrey() +{ + LOG(RPiAwb, Debug) << "Grey world AWB"; + /* + * Make a separate list of the derivatives for each of red and blue, so + * that we can sort them to exclude the extreme gains. We could + * consider some variations, such as normalising all the zones first, or + * doing an L2 average etc. + */ + std::vector &derivsR(zones_); + std::vector derivsB(derivsR); + std::sort(derivsR.begin(), derivsR.end(), + [](RGB const &a, RGB const &b) { + return a.G * b.R < b.G * a.R; + }); + std::sort(derivsB.begin(), derivsB.end(), + [](RGB const &a, RGB const &b) { + return a.G * b.B < b.G * a.B; + }); + /* Average the middle half of the values. */ + int discard = derivsR.size() / 4; + RGB sumR(0, 0, 0), sumB(0, 0, 0); + for (auto ri = derivsR.begin() + discard, + bi = derivsB.begin() + discard; + ri != derivsR.end() - discard; ri++, bi++) + sumR += *ri, sumB += *bi; + double gainR = sumR.G / (sumR.R + 1), + gainB = sumB.G / (sumB.B + 1); + asyncResults_.temperatureK = 4500; /* don't know what it is */ + asyncResults_.gainR = gainR; + asyncResults_.gainG = 1.0; + asyncResults_.gainB = gainB; +} + +void Awb::doAwb() +{ + prepareStats(); + LOG(RPiAwb, Debug) << "Valid zones: " << zones_.size(); + if (zones_.size() > config_.minRegions) { + if (config_.bayes) + awbBayes(); + else + awbGrey(); + LOG(RPiAwb, Debug) + << "CT found is " + << asyncResults_.temperatureK + << " with gains r " << asyncResults_.gainR + << " and b " << asyncResults_.gainB; + } + /* + * we're done with these; we may as well relinquish our hold on the + * pointer. + */ + statistics_.reset(); +} + +/* Register algorithm with the system. */ +static Algorithm *create(Controller *controller) +{ + return (Algorithm *)new Awb(controller); +} +static RegisterAlgorithm reg(NAME, &create); diff --git a/src/ipa/rpi/controller/rpi/awb.h b/src/ipa/rpi/controller/rpi/awb.h new file mode 100644 index 00000000..e7d49cd8 --- /dev/null +++ b/src/ipa/rpi/controller/rpi/awb.h @@ -0,0 +1,191 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (C) 2019, Raspberry Pi Ltd + * + * awb.h - AWB control algorithm + */ +#pragma once + +#include +#include +#include + +#include "../awb_algorithm.h" +#include "../pwl.h" +#include "../awb_status.h" +#include "../statistics.h" + +namespace RPiController { + +/* Control algorithm to perform AWB calculations. */ + +struct AwbMode { + int read(const libcamera::YamlObject ¶ms); + double ctLo; /* low CT value for search */ + double ctHi; /* high CT value for search */ +}; + +struct AwbPrior { + int read(const libcamera::YamlObject ¶ms); + double lux; /* lux level */ + Pwl prior; /* maps CT to prior log likelihood for this lux level */ +}; + +struct AwbConfig { + AwbConfig() : defaultMode(nullptr) {} + int read(const libcamera::YamlObject ¶ms); + /* Only repeat the AWB calculation every "this many" frames */ + uint16_t framePeriod; + /* number of initial frames for which speed taken as 1.0 (maximum) */ + uint16_t startupFrames; + unsigned int convergenceFrames; /* approx number of frames to converge */ + double speed; /* IIR filter speed applied to algorithm results */ + bool fast; /* "fast" mode uses a 16x16 rather than 32x32 grid */ + Pwl ctR; /* function maps CT to r (= R/G) */ + Pwl ctB; /* function maps CT to b (= B/G) */ + Pwl ctRInverse; /* inverse of ctR */ + Pwl ctBInverse; /* inverse of ctB */ + /* table of illuminant priors at different lux levels */ + std::vector priors; + /* AWB "modes" (determines the search range) */ + std::map modes; + AwbMode *defaultMode; /* mode used if no mode selected */ + /* + * minimum proportion of pixels counted within AWB region for it to be + * "useful" + */ + double minPixels; + /* minimum G value of those pixels, to be regarded a "useful" */ + uint16_t minG; + /* + * number of AWB regions that must be "useful" in order to do the AWB + * calculation + */ + uint32_t minRegions; + /* clamp on colour error term (so as not to penalise non-grey excessively) */ + double deltaLimit; + /* step size control in coarse search */ + double coarseStep; + /* how far to wander off CT curve towards "more purple" */ + double transversePos; + /* how far to wander off CT curve towards "more green" */ + double transverseNeg; + /* + * red sensitivity ratio (set to canonical sensor's R/G divided by this + * sensor's R/G) + */ + double sensitivityR; + /* + * blue sensitivity ratio (set to canonical sensor's B/G divided by this + * sensor's B/G) + */ + double sensitivityB; + /* The whitepoint (which we normally "aim" for) can be moved. */ + double whitepointR; + double whitepointB; + bool bayes; /* use Bayesian algorithm */ +}; + +class Awb : public AwbAlgorithm +{ +public: + Awb(Controller *controller = NULL); + ~Awb(); + char const *name() const override; + void initialise() override; + int read(const libcamera::YamlObject ¶ms) override; + unsigned int getConvergenceFrames() const override; + void setMode(std::string const &name) override; + void setManualGains(double manualR, double manualB) override; + void enableAuto() override; + void disableAuto() override; + void switchMode(CameraMode const &cameraMode, Metadata *metadata) override; + void prepare(Metadata *imageMetadata) override; + void process(StatisticsPtr &stats, Metadata *imageMetadata) override; + struct RGB { + RGB(double r = 0, double g = 0, double b = 0) + : R(r), G(g), B(b) + { + } + double R, G, B; + RGB &operator+=(RGB const &other) + { + R += other.R, G += other.G, B += other.B; + return *this; + } + }; + +private: + bool isAutoEnabled() const; + /* configuration is read-only, and available to both threads */ + AwbConfig config_; + std::thread asyncThread_; + void asyncFunc(); /* asynchronous thread function */ + std::mutex mutex_; + /* condvar for async thread to wait on */ + std::condition_variable asyncSignal_; + /* condvar for synchronous thread to wait on */ + std::condition_variable syncSignal_; + /* for sync thread to check if async thread finished (requires mutex) */ + bool asyncFinished_; + /* for async thread to check if it's been told to run (requires mutex) */ + bool asyncStart_; + /* for async thread to check if it's been told to quit (requires mutex) */ + bool asyncAbort_; + + /* + * The following are only for the synchronous thread to use: + * for sync thread to note its has asked async thread to run + */ + bool asyncStarted_; + /* counts up to framePeriod before restarting the async thread */ + int framePhase_; + int frameCount_; /* counts up to startup_frames */ + AwbStatus syncResults_; + AwbStatus prevSyncResults_; + std::string modeName_; + /* + * The following are for the asynchronous thread to use, though the main + * thread can set/reset them if the async thread is known to be idle: + */ + void restartAsync(StatisticsPtr &stats, double lux); + /* copy out the results from the async thread so that it can be restarted */ + void fetchAsyncResults(); + StatisticsPtr statistics_; + AwbMode *mode_; + double lux_; + AwbStatus asyncResults_; + void doAwb(); + void awbBayes(); + void awbGrey(); + void prepareStats(); + double computeDelta2Sum(double gainR, double gainB); + Pwl interpolatePrior(); + double coarseSearch(Pwl const &prior); + void fineSearch(double &t, double &r, double &b, Pwl const &prior); + std::vector zones_; + std::vector points_; + /* manual r setting */ + double manualR_; + /* manual b setting */ + double manualB_; +}; + +static inline Awb::RGB operator+(Awb::RGB const &a, Awb::RGB const &b) +{ + return Awb::RGB(a.R + b.R, a.G + b.G, a.B + b.B); +} +static inline Awb::RGB operator-(Awb::RGB const &a, Awb::RGB const &b) +{ + return Awb::RGB(a.R - b.R, a.G - b.G, a.B - b.B); +} +static inline Awb::RGB operator*(double d, Awb::RGB const &rgb) +{ + return Awb::RGB(d * rgb.R, d * rgb.G, d * rgb.B); +} +static inline Awb::RGB operator*(Awb::RGB const &rgb, double d) +{ + return d * rgb; +} + +} /* namespace RPiController */ diff --git a/src/ipa/rpi/controller/rpi/black_level.cpp b/src/ipa/rpi/controller/rpi/black_level.cpp new file mode 100644 index 00000000..85baec3f --- /dev/null +++ b/src/ipa/rpi/controller/rpi/black_level.cpp @@ -0,0 +1,66 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (C) 2019, Raspberry Pi Ltd + * + * black_level.cpp - black level control algorithm + */ + +#include +#include + +#include + +#include "../black_level_status.h" + +#include "black_level.h" + +using namespace RPiController; +using namespace libcamera; + +LOG_DEFINE_CATEGORY(RPiBlackLevel) + +#define NAME "rpi.black_level" + +BlackLevel::BlackLevel(Controller *controller) + : Algorithm(controller) +{ +} + +char const *BlackLevel::name() const +{ + return NAME; +} + +int BlackLevel::read(const libcamera::YamlObject ¶ms) +{ + /* 64 in 10 bits scaled to 16 bits */ + uint16_t blackLevel = params["black_level"].get(4096); + blackLevelR_ = params["black_level_r"].get(blackLevel); + blackLevelG_ = params["black_level_g"].get(blackLevel); + blackLevelB_ = params["black_level_b"].get(blackLevel); + LOG(RPiBlackLevel, Debug) + << " Read black levels red " << blackLevelR_ + << " green " << blackLevelG_ + << " blue " << blackLevelB_; + return 0; +} + +void BlackLevel::prepare(Metadata *imageMetadata) +{ + /* + * Possibly we should think about doing this in a switchMode or + * something? + */ + struct BlackLevelStatus status; + status.blackLevelR = blackLevelR_; + status.blackLevelG = blackLevelG_; + status.blackLevelB = blackLevelB_; + imageMetadata->set("black_level.status", status); +} + +/* Register algorithm with the system. */ +static Algorithm *create(Controller *controller) +{ + return new BlackLevel(controller); +} +static RegisterAlgorithm reg(NAME, &create); diff --git a/src/ipa/rpi/controller/rpi/black_level.h b/src/ipa/rpi/controller/rpi/black_level.h new file mode 100644 index 00000000..2403f7f7 --- /dev/null +++ b/src/ipa/rpi/controller/rpi/black_level.h @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (C) 2019, Raspberry Pi Ltd + * + * black_level.h - black level control algorithm + */ +#pragma once + +#include "../algorithm.h" +#include "../black_level_status.h" + +/* This is our implementation of the "black level algorithm". */ + +namespace RPiController { + +class BlackLevel : public Algorithm +{ +public: + BlackLevel(Controller *controller); + char const *name() const override; + int read(const libcamera::YamlObject ¶ms) override; + void prepare(Metadata *imageMetadata) override; + +private: + double blackLevelR_; + double blackLevelG_; + double blackLevelB_; +}; + +} /* namespace RPiController */ diff --git a/src/ipa/rpi/controller/rpi/ccm.cpp b/src/ipa/rpi/controller/rpi/ccm.cpp new file mode 100644 index 00000000..2e2e6664 --- /dev/null +++ b/src/ipa/rpi/controller/rpi/ccm.cpp @@ -0,0 +1,199 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (C) 2019, Raspberry Pi Ltd + * + * ccm.cpp - CCM (colour correction matrix) control algorithm + */ + +#include + +#include "../awb_status.h" +#include "../ccm_status.h" +#include "../lux_status.h" +#include "../metadata.h" + +#include "ccm.h" + +using namespace RPiController; +using namespace libcamera; + +LOG_DEFINE_CATEGORY(RPiCcm) + +/* + * This algorithm selects a CCM (Colour Correction Matrix) according to the + * colour temperature estimated by AWB (interpolating between known matricies as + * necessary). Additionally the amount of colour saturation can be controlled + * both according to the current estimated lux level and according to a + * saturation setting that is exposed to applications. + */ + +#define NAME "rpi.ccm" + +Matrix::Matrix() +{ + memset(m, 0, sizeof(m)); +} +Matrix::Matrix(double m0, double m1, double m2, double m3, double m4, double m5, + double m6, double m7, double m8) +{ + m[0][0] = m0, m[0][1] = m1, m[0][2] = m2, m[1][0] = m3, m[1][1] = m4, + m[1][2] = m5, m[2][0] = m6, m[2][1] = m7, m[2][2] = m8; +} +int Matrix::read(const libcamera::YamlObject ¶ms) +{ + double *ptr = (double *)m; + + if (params.size() != 9) { + LOG(RPiCcm, Error) << "Wrong number of values in CCM"; + return -EINVAL; + } + + for (const auto ¶m : params.asList()) { + auto value = param.get(); + if (!value) + return -EINVAL; + *ptr++ = *value; + } + + return 0; +} + +Ccm::Ccm(Controller *controller) + : CcmAlgorithm(controller), saturation_(1.0) {} + +char const *Ccm::name() const +{ + return NAME; +} + +int Ccm::read(const libcamera::YamlObject ¶ms) +{ + int ret; + + if (params.contains("saturation")) { + ret = config_.saturation.read(params["saturation"]); + if (ret) + return ret; + } + + for (auto &p : params["ccms"].asList()) { + auto value = p["ct"].get(); + if (!value) + return -EINVAL; + + CtCcm ctCcm; + ctCcm.ct = *value; + ret = ctCcm.ccm.read(p["ccm"]); + if (ret) + return ret; + + if (!config_.ccms.empty() && ctCcm.ct <= config_.ccms.back().ct) { + LOG(RPiCcm, Error) + << "CCM not in increasing colour temperature order"; + return -EINVAL; + } + + config_.ccms.push_back(std::move(ctCcm)); + } + + if (config_.ccms.empty()) { + LOG(RPiCcm, Error) << "No CCMs specified"; + return -EINVAL; + } + + return 0; +} + +void Ccm::setSaturation(double saturation) +{ + saturation_ = saturation; +} + +void Ccm::initialise() +{ +} + +template +static bool getLocked(Metadata *metadata, std::string const &tag, T &value) +{ + T *ptr = metadata->getLocked(tag); + if (ptr == nullptr) + return false; + value = *ptr; + return true; +} + +Matrix calculateCcm(std::vector const &ccms, double ct) +{ + if (ct <= ccms.front().ct) + return ccms.front().ccm; + else if (ct >= ccms.back().ct) + return ccms.back().ccm; + else { + int i = 0; + for (; ct > ccms[i].ct; i++) + ; + double lambda = + (ct - ccms[i - 1].ct) / (ccms[i].ct - ccms[i - 1].ct); + return lambda * ccms[i].ccm + (1.0 - lambda) * ccms[i - 1].ccm; + } +} + +Matrix applySaturation(Matrix const &ccm, double saturation) +{ + Matrix RGB2Y(0.299, 0.587, 0.114, -0.169, -0.331, 0.500, 0.500, -0.419, + -0.081); + Matrix Y2RGB(1.000, 0.000, 1.402, 1.000, -0.345, -0.714, 1.000, 1.771, + 0.000); + Matrix S(1, 0, 0, 0, saturation, 0, 0, 0, saturation); + return Y2RGB * S * RGB2Y * ccm; +} + +void Ccm::prepare(Metadata *imageMetadata) +{ + bool awbOk = false, luxOk = false; + struct AwbStatus awb = {}; + awb.temperatureK = 4000; /* in case no metadata */ + struct LuxStatus lux = {}; + lux.lux = 400; /* in case no metadata */ + { + /* grab mutex just once to get everything */ + std::lock_guard lock(*imageMetadata); + awbOk = getLocked(imageMetadata, "awb.status", awb); + luxOk = getLocked(imageMetadata, "lux.status", lux); + } + if (!awbOk) + LOG(RPiCcm, Warning) << "no colour temperature found"; + if (!luxOk) + LOG(RPiCcm, Warning) << "no lux value found"; + Matrix ccm = calculateCcm(config_.ccms, awb.temperatureK); + double saturation = saturation_; + struct CcmStatus ccmStatus; + ccmStatus.saturation = saturation; + if (!config_.saturation.empty()) + saturation *= config_.saturation.eval( + config_.saturation.domain().clip(lux.lux)); + ccm = applySaturation(ccm, saturation); + for (int j = 0; j < 3; j++) + for (int i = 0; i < 3; i++) + ccmStatus.matrix[j * 3 + i] = + std::max(-8.0, std::min(7.9999, ccm.m[j][i])); + LOG(RPiCcm, Debug) + << "colour temperature " << awb.temperatureK << "K"; + LOG(RPiCcm, Debug) + << "CCM: " << ccmStatus.matrix[0] << " " << ccmStatus.matrix[1] + << " " << ccmStatus.matrix[2] << " " + << ccmStatus.matrix[3] << " " << ccmStatus.matrix[4] + << " " << ccmStatus.matrix[5] << " " + << ccmStatus.matrix[6] << " " << ccmStatus.matrix[7] + << " " << ccmStatus.matrix[8]; + imageMetadata->set("ccm.status", ccmStatus); +} + +/* Register algorithm with the system. */ +static Algorithm *create(Controller *controller) +{ + return (Algorithm *)new Ccm(controller); + ; +} +static RegisterAlgorithm reg(NAME, &create); diff --git a/src/ipa/rpi/controller/rpi/ccm.h b/src/ipa/rpi/controller/rpi/ccm.h new file mode 100644 index 00000000..286d0b33 --- /dev/null +++ b/src/ipa/rpi/controller/rpi/ccm.h @@ -0,0 +1,75 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (C) 2019, Raspberry Pi Ltd + * + * ccm.h - CCM (colour correction matrix) control algorithm + */ +#pragma once + +#include + +#include "../ccm_algorithm.h" +#include "../pwl.h" + +namespace RPiController { + +/* Algorithm to calculate colour matrix. Should be placed after AWB. */ + +struct Matrix { + Matrix(double m0, double m1, double m2, double m3, double m4, double m5, + double m6, double m7, double m8); + Matrix(); + double m[3][3]; + int read(const libcamera::YamlObject ¶ms); +}; +static inline Matrix operator*(double d, Matrix const &m) +{ + return Matrix(m.m[0][0] * d, m.m[0][1] * d, m.m[0][2] * d, + m.m[1][0] * d, m.m[1][1] * d, m.m[1][2] * d, + m.m[2][0] * d, m.m[2][1] * d, m.m[2][2] * d); +} +static inline Matrix operator*(Matrix const &m1, Matrix const &m2) +{ + Matrix m; + for (int i = 0; i < 3; i++) + for (int j = 0; j < 3; j++) + m.m[i][j] = m1.m[i][0] * m2.m[0][j] + + m1.m[i][1] * m2.m[1][j] + + m1.m[i][2] * m2.m[2][j]; + return m; +} +static inline Matrix operator+(Matrix const &m1, Matrix const &m2) +{ + Matrix m; + for (int i = 0; i < 3; i++) + for (int j = 0; j < 3; j++) + m.m[i][j] = m1.m[i][j] + m2.m[i][j]; + return m; +} + +struct CtCcm { + double ct; + Matrix ccm; +}; + +struct CcmConfig { + std::vector ccms; + Pwl saturation; +}; + +class Ccm : public CcmAlgorithm +{ +public: + Ccm(Controller *controller = NULL); + char const *name() const override; + int read(const libcamera::YamlObject ¶ms) override; + void setSaturation(double saturation) override; + void initialise() override; + void prepare(Metadata *imageMetadata) override; + +private: + CcmConfig config_; + double saturation_; +}; + +} /* namespace RPiController */ diff --git a/src/ipa/rpi/controller/rpi/contrast.cpp b/src/ipa/rpi/controller/rpi/contrast.cpp new file mode 100644 index 00000000..bee1eadd --- /dev/null +++ b/src/ipa/rpi/controller/rpi/contrast.cpp @@ -0,0 +1,181 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (C) 2019, Raspberry Pi Ltd + * + * contrast.cpp - contrast (gamma) control algorithm + */ +#include + +#include + +#include "../contrast_status.h" +#include "../histogram.h" + +#include "contrast.h" + +using namespace RPiController; +using namespace libcamera; + +LOG_DEFINE_CATEGORY(RPiContrast) + +/* + * This is a very simple control algorithm which simply retrieves the results of + * AGC and AWB via their "status" metadata, and applies digital gain to the + * colour channels in accordance with those instructions. We take care never to + * apply less than unity gains, as that would cause fully saturated pixels to go + * off-white. + */ + +#define NAME "rpi.contrast" + +Contrast::Contrast(Controller *controller) + : ContrastAlgorithm(controller), brightness_(0.0), contrast_(1.0) +{ +} + +char const *Contrast::name() const +{ + return NAME; +} + +int Contrast::read(const libcamera::YamlObject ¶ms) +{ + // enable adaptive enhancement by default + config_.ceEnable = params["ce_enable"].get(1); + // the point near the bottom of the histogram to move + config_.loHistogram = params["lo_histogram"].get(0.01); + // where in the range to try and move it to + config_.loLevel = params["lo_level"].get(0.015); + // but don't move by more than this + config_.loMax = params["lo_max"].get(500); + // equivalent values for the top of the histogram... + config_.hiHistogram = params["hi_histogram"].get(0.95); + config_.hiLevel = params["hi_level"].get(0.95); + config_.hiMax = params["hi_max"].get(2000); + return config_.gammaCurve.read(params["gamma_curve"]); +} + +void Contrast::setBrightness(double brightness) +{ + brightness_ = brightness; +} + +void Contrast::setContrast(double contrast) +{ + contrast_ = contrast; +} + +void Contrast::initialise() +{ + /* + * Fill in some default values as Prepare will run before Process gets + * called. + */ + status_.brightness = brightness_; + status_.contrast = contrast_; + status_.gammaCurve = config_.gammaCurve; +} + +void Contrast::prepare(Metadata *imageMetadata) +{ + imageMetadata->set("contrast.status", status_); +} + +Pwl computeStretchCurve(Histogram const &histogram, + ContrastConfig const &config) +{ + Pwl enhance; + enhance.append(0, 0); + /* + * If the start of the histogram is rather empty, try to pull it down a + * bit. + */ + double histLo = histogram.quantile(config.loHistogram) * + (65536 / histogram.bins()); + double levelLo = config.loLevel * 65536; + LOG(RPiContrast, Debug) + << "Move histogram point " << histLo << " to " << levelLo; + histLo = std::max(levelLo, + std::min(65535.0, std::min(histLo, levelLo + config.loMax))); + LOG(RPiContrast, Debug) + << "Final values " << histLo << " -> " << levelLo; + enhance.append(histLo, levelLo); + /* + * Keep the mid-point (median) in the same place, though, to limit the + * apparent amount of global brightness shift. + */ + double mid = histogram.quantile(0.5) * (65536 / histogram.bins()); + enhance.append(mid, mid); + + /* + * If the top to the histogram is empty, try to pull the pixel values + * there up. + */ + double histHi = histogram.quantile(config.hiHistogram) * + (65536 / histogram.bins()); + double levelHi = config.hiLevel * 65536; + LOG(RPiContrast, Debug) + << "Move histogram point " << histHi << " to " << levelHi; + histHi = std::min(levelHi, + std::max(0.0, std::max(histHi, levelHi - config.hiMax))); + LOG(RPiContrast, Debug) + << "Final values " << histHi << " -> " << levelHi; + enhance.append(histHi, levelHi); + enhance.append(65535, 65535); + return enhance; +} + +Pwl applyManualContrast(Pwl const &gammaCurve, double brightness, + double contrast) +{ + Pwl newGammaCurve; + LOG(RPiContrast, Debug) + << "Manual brightness " << brightness << " contrast " << contrast; + gammaCurve.map([&](double x, double y) { + newGammaCurve.append( + x, std::max(0.0, std::min(65535.0, + (y - 32768) * contrast + + 32768 + brightness))); + }); + return newGammaCurve; +} + +void Contrast::process(StatisticsPtr &stats, + [[maybe_unused]] Metadata *imageMetadata) +{ + Histogram &histogram = stats->yHist; + /* + * We look at the histogram and adjust the gamma curve in the following + * ways: 1. Adjust the gamma curve so as to pull the start of the + * histogram down, and possibly push the end up. + */ + Pwl gammaCurve = config_.gammaCurve; + if (config_.ceEnable) { + if (config_.loMax != 0 || config_.hiMax != 0) + gammaCurve = computeStretchCurve(histogram, config_).compose(gammaCurve); + /* + * We could apply other adjustments (e.g. partial equalisation) + * based on the histogram...? + */ + } + /* + * 2. Finally apply any manually selected brightness/contrast + * adjustment. + */ + if (brightness_ != 0 || contrast_ != 1.0) + gammaCurve = applyManualContrast(gammaCurve, brightness_, contrast_); + /* + * And fill in the status for output. Use more points towards the bottom + * of the curve. + */ + status_.brightness = brightness_; + status_.contrast = contrast_; + status_.gammaCurve = std::move(gammaCurve); +} + +/* Register algorithm with the system. */ +static Algorithm *create(Controller *controller) +{ + return (Algorithm *)new Contrast(controller); +} +static RegisterAlgorithm reg(NAME, &create); diff --git a/src/ipa/rpi/controller/rpi/contrast.h b/src/ipa/rpi/controller/rpi/contrast.h new file mode 100644 index 00000000..9c81277a --- /dev/null +++ b/src/ipa/rpi/controller/rpi/contrast.h @@ -0,0 +1,51 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (C) 2019, Raspberry Pi Ltd + * + * contrast.h - contrast (gamma) control algorithm + */ +#pragma once + +#include + +#include "../contrast_algorithm.h" +#include "../pwl.h" + +namespace RPiController { + +/* + * Back End algorithm to appaly correct digital gain. Should be placed after + * Back End AWB. + */ + +struct ContrastConfig { + bool ceEnable; + double loHistogram; + double loLevel; + double loMax; + double hiHistogram; + double hiLevel; + double hiMax; + Pwl gammaCurve; +}; + +class Contrast : public ContrastAlgorithm +{ +public: + Contrast(Controller *controller = NULL); + char const *name() const override; + int read(const libcamera::YamlObject ¶ms) override; + void setBrightness(double brightness) override; + void setContrast(double contrast) override; + void initialise() override; + void prepare(Metadata *imageMetadata) override; + void process(StatisticsPtr &stats, Metadata *imageMetadata) override; + +private: + ContrastConfig config_; + double brightness_; + double contrast_; + ContrastStatus status_; +}; + +} /* namespace RPiController */ diff --git a/src/ipa/rpi/controller/rpi/dpc.cpp b/src/ipa/rpi/controller/rpi/dpc.cpp new file mode 100644 index 00000000..be3871df --- /dev/null +++ b/src/ipa/rpi/controller/rpi/dpc.cpp @@ -0,0 +1,59 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (C) 2019, Raspberry Pi Ltd + * + * dpc.cpp - DPC (defective pixel correction) control algorithm + */ + +#include + +#include "dpc.h" + +using namespace RPiController; +using namespace libcamera; + +LOG_DEFINE_CATEGORY(RPiDpc) + +/* + * We use the lux status so that we can apply stronger settings in darkness (if + * necessary). + */ + +#define NAME "rpi.dpc" + +Dpc::Dpc(Controller *controller) + : Algorithm(controller) +{ +} + +char const *Dpc::name() const +{ + return NAME; +} + +int Dpc::read(const libcamera::YamlObject ¶ms) +{ + config_.strength = params["strength"].get(1); + if (config_.strength < 0 || config_.strength > 2) { + LOG(RPiDpc, Error) << "Bad strength value"; + return -EINVAL; + } + + return 0; +} + +void Dpc::prepare(Metadata *imageMetadata) +{ + DpcStatus dpcStatus = {}; + /* Should we vary this with lux level or analogue gain? TBD. */ + dpcStatus.strength = config_.strength; + LOG(RPiDpc, Debug) << "strength " << dpcStatus.strength; + imageMetadata->set("dpc.status", dpcStatus); +} + +/* Register algorithm with the system. */ +static Algorithm *create(Controller *controller) +{ + return (Algorithm *)new Dpc(controller); +} +static RegisterAlgorithm reg(NAME, &create); diff --git a/src/ipa/rpi/controller/rpi/dpc.h b/src/ipa/rpi/controller/rpi/dpc.h new file mode 100644 index 00000000..84a05604 --- /dev/null +++ b/src/ipa/rpi/controller/rpi/dpc.h @@ -0,0 +1,32 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (C) 2019, Raspberry Pi Ltd + * + * dpc.h - DPC (defective pixel correction) control algorithm + */ +#pragma once + +#include "../algorithm.h" +#include "../dpc_status.h" + +namespace RPiController { + +/* Back End algorithm to apply appropriate GEQ settings. */ + +struct DpcConfig { + int strength; +}; + +class Dpc : public Algorithm +{ +public: + Dpc(Controller *controller); + char const *name() const override; + int read(const libcamera::YamlObject ¶ms) override; + void prepare(Metadata *imageMetadata) override; + +private: + DpcConfig config_; +}; + +} /* namespace RPiController */ diff --git a/src/ipa/rpi/controller/rpi/focus.h b/src/ipa/rpi/controller/rpi/focus.h new file mode 100644 index 00000000..8556039d --- /dev/null +++ b/src/ipa/rpi/controller/rpi/focus.h @@ -0,0 +1,28 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (C) 2020, Raspberry Pi Ltd + * + * focus.h - focus algorithm + */ +#pragma once + +#include "../algorithm.h" +#include "../metadata.h" + +/* + * The "focus" algorithm. All it does it print out a version of the + * focus contrast measure; there is no actual auto-focus mechanism to + * control. + */ + +namespace RPiController { + +class Focus : public Algorithm +{ +public: + Focus(Controller *controller); + char const *name() const override; + void process(StatisticsPtr &stats, Metadata *imageMetadata) override; +}; + +} /* namespace RPiController */ diff --git a/src/ipa/rpi/controller/rpi/geq.cpp b/src/ipa/rpi/controller/rpi/geq.cpp new file mode 100644 index 00000000..510870e9 --- /dev/null +++ b/src/ipa/rpi/controller/rpi/geq.cpp @@ -0,0 +1,89 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (C) 2019, Raspberry Pi Ltd + * + * geq.cpp - GEQ (green equalisation) control algorithm + */ + +#include + +#include "../device_status.h" +#include "../lux_status.h" +#include "../pwl.h" + +#include "geq.h" + +using namespace RPiController; +using namespace libcamera; + +LOG_DEFINE_CATEGORY(RPiGeq) + +/* + * We use the lux status so that we can apply stronger settings in darkness (if + * necessary). + */ + +#define NAME "rpi.geq" + +Geq::Geq(Controller *controller) + : Algorithm(controller) +{ +} + +char const *Geq::name() const +{ + return NAME; +} + +int Geq::read(const libcamera::YamlObject ¶ms) +{ + config_.offset = params["offset"].get(0); + config_.slope = params["slope"].get(0.0); + if (config_.slope < 0.0 || config_.slope >= 1.0) { + LOG(RPiGeq, Error) << "Bad slope value"; + return -EINVAL; + } + + if (params.contains("strength")) { + int ret = config_.strength.read(params["strength"]); + if (ret) + return ret; + } + + return 0; +} + +void Geq::prepare(Metadata *imageMetadata) +{ + LuxStatus luxStatus = {}; + luxStatus.lux = 400; + if (imageMetadata->get("lux.status", luxStatus)) + LOG(RPiGeq, Warning) << "no lux data found"; + DeviceStatus deviceStatus; + deviceStatus.analogueGain = 1.0; /* in case not found */ + if (imageMetadata->get("device.status", deviceStatus)) + LOG(RPiGeq, Warning) + << "no device metadata - use analogue gain of 1x"; + GeqStatus geqStatus = {}; + double strength = config_.strength.empty() + ? 1.0 + : config_.strength.eval(config_.strength.domain().clip(luxStatus.lux)); + strength *= deviceStatus.analogueGain; + double offset = config_.offset * strength; + double slope = config_.slope * strength; + geqStatus.offset = std::min(65535.0, std::max(0.0, offset)); + geqStatus.slope = std::min(.99999, std::max(0.0, slope)); + LOG(RPiGeq, Debug) + << "offset " << geqStatus.offset << " slope " + << geqStatus.slope << " (analogue gain " + << deviceStatus.analogueGain << " lux " + << luxStatus.lux << ")"; + imageMetadata->set("geq.status", geqStatus); +} + +/* Register algorithm with the system. */ +static Algorithm *create(Controller *controller) +{ + return (Algorithm *)new Geq(controller); +} +static RegisterAlgorithm reg(NAME, &create); diff --git a/src/ipa/rpi/controller/rpi/geq.h b/src/ipa/rpi/controller/rpi/geq.h new file mode 100644 index 00000000..ee3a52ff --- /dev/null +++ b/src/ipa/rpi/controller/rpi/geq.h @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (C) 2019, Raspberry Pi Ltd + * + * geq.h - GEQ (green equalisation) control algorithm + */ +#pragma once + +#include "../algorithm.h" +#include "../geq_status.h" + +namespace RPiController { + +/* Back End algorithm to apply appropriate GEQ settings. */ + +struct GeqConfig { + uint16_t offset; + double slope; + Pwl strength; /* lux to strength factor */ +}; + +class Geq : public Algorithm +{ +public: + Geq(Controller *controller); + char const *name() const override; + int read(const libcamera::YamlObject ¶ms) override; + void prepare(Metadata *imageMetadata) override; + +private: + GeqConfig config_; +}; + +} /* namespace RPiController */ diff --git a/src/ipa/rpi/controller/rpi/lux.cpp b/src/ipa/rpi/controller/rpi/lux.cpp new file mode 100644 index 00000000..06625f3a --- /dev/null +++ b/src/ipa/rpi/controller/rpi/lux.cpp @@ -0,0 +1,115 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (C) 2019, Raspberry Pi Ltd + * + * lux.cpp - Lux control algorithm + */ +#include + +#include + +#include "../device_status.h" + +#include "lux.h" + +using namespace RPiController; +using namespace libcamera; +using namespace std::literals::chrono_literals; + +LOG_DEFINE_CATEGORY(RPiLux) + +#define NAME "rpi.lux" + +Lux::Lux(Controller *controller) + : Algorithm(controller) +{ + /* + * Put in some defaults as there will be no meaningful values until + * Process has run. + */ + status_.aperture = 1.0; + status_.lux = 400; +} + +char const *Lux::name() const +{ + return NAME; +} + +int Lux::read(const libcamera::YamlObject ¶ms) +{ + auto value = params["reference_shutter_speed"].get(); + if (!value) + return -EINVAL; + referenceShutterSpeed_ = *value * 1.0us; + + value = params["reference_gain"].get(); + if (!value) + return -EINVAL; + referenceGain_ = *value; + + referenceAperture_ = params["reference_aperture"].get(1.0); + + value = params["reference_Y"].get(); + if (!value) + return -EINVAL; + referenceY_ = *value; + + value = params["reference_lux"].get(); + if (!value) + return -EINVAL; + referenceLux_ = *value; + + currentAperture_ = referenceAperture_; + return 0; +} + +void Lux::setCurrentAperture(double aperture) +{ + currentAperture_ = aperture; +} + +void Lux::prepare(Metadata *imageMetadata) +{ + std::unique_lock lock(mutex_); + imageMetadata->set("lux.status", status_); +} + +void Lux::process(StatisticsPtr &stats, Metadata *imageMetadata) +{ + DeviceStatus deviceStatus; + if (imageMetadata->get("device.status", deviceStatus) == 0) { + double currentGain = deviceStatus.analogueGain; + double currentAperture = deviceStatus.aperture.value_or(currentAperture_); + double currentY = stats->yHist.interQuantileMean(0, 1); + double gainRatio = referenceGain_ / currentGain; + double shutterSpeedRatio = + referenceShutterSpeed_ / deviceStatus.shutterSpeed; + double apertureRatio = referenceAperture_ / currentAperture; + double yRatio = currentY * (65536 / stats->yHist.bins()) / referenceY_; + double estimatedLux = shutterSpeedRatio * gainRatio * + apertureRatio * apertureRatio * + yRatio * referenceLux_; + LuxStatus status; + status.lux = estimatedLux; + status.aperture = currentAperture; + LOG(RPiLux, Debug) << ": estimated lux " << estimatedLux; + { + std::unique_lock lock(mutex_); + status_ = status; + } + /* + * Overwrite the metadata here as well, so that downstream + * algorithms get the latest value. + */ + imageMetadata->set("lux.status", status); + } else + LOG(RPiLux, Warning) << ": no device metadata"; +} + +/* Register algorithm with the system. */ +static Algorithm *create(Controller *controller) +{ + return (Algorithm *)new Lux(controller); +} +static RegisterAlgorithm reg(NAME, &create); diff --git a/src/ipa/rpi/controller/rpi/lux.h b/src/ipa/rpi/controller/rpi/lux.h new file mode 100644 index 00000000..89411a54 --- /dev/null +++ b/src/ipa/rpi/controller/rpi/lux.h @@ -0,0 +1,45 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (C) 2019, Raspberry Pi Ltd + * + * lux.h - Lux control algorithm + */ +#pragma once + +#include + +#include + +#include "../lux_status.h" +#include "../algorithm.h" + +/* This is our implementation of the "lux control algorithm". */ + +namespace RPiController { + +class Lux : public Algorithm +{ +public: + Lux(Controller *controller); + char const *name() const override; + int read(const libcamera::YamlObject ¶ms) override; + void prepare(Metadata *imageMetadata) override; + void process(StatisticsPtr &stats, Metadata *imageMetadata) override; + void setCurrentAperture(double aperture); + +private: + /* + * These values define the conditions of the reference image, against + * which we compare the new image. + */ + libcamera::utils::Duration referenceShutterSpeed_; + double referenceGain_; + double referenceAperture_; /* units of 1/f */ + double referenceY_; /* out of 65536 */ + double referenceLux_; + double currentAperture_; + LuxStatus status_; + std::mutex mutex_; +}; + +} /* namespace RPiController */ diff --git a/src/ipa/rpi/controller/rpi/noise.cpp b/src/ipa/rpi/controller/rpi/noise.cpp new file mode 100644 index 00000000..bcd8b9ed --- /dev/null +++ b/src/ipa/rpi/controller/rpi/noise.cpp @@ -0,0 +1,89 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (C) 2019, Raspberry Pi Ltd + * + * noise.cpp - Noise control algorithm + */ + +#include + +#include + +#include "../device_status.h" +#include "../noise_status.h" + +#include "noise.h" + +using namespace RPiController; +using namespace libcamera; + +LOG_DEFINE_CATEGORY(RPiNoise) + +#define NAME "rpi.noise" + +Noise::Noise(Controller *controller) + : Algorithm(controller), modeFactor_(1.0) +{ +} + +char const *Noise::name() const +{ + return NAME; +} + +void Noise::switchMode(CameraMode const &cameraMode, + [[maybe_unused]] Metadata *metadata) +{ + /* + * For example, we would expect a 2x2 binned mode to have a "noise + * factor" of sqrt(2x2) = 2. (can't be less than one, right?) + */ + modeFactor_ = std::max(1.0, cameraMode.noiseFactor); +} + +int Noise::read(const libcamera::YamlObject ¶ms) +{ + auto value = params["reference_constant"].get(); + if (!value) + return -EINVAL; + referenceConstant_ = *value; + + value = params["reference_slope"].get(); + if (!value) + return -EINVAL; + referenceSlope_ = *value; + + return 0; +} + +void Noise::prepare(Metadata *imageMetadata) +{ + struct DeviceStatus deviceStatus; + deviceStatus.analogueGain = 1.0; /* keep compiler calm */ + if (imageMetadata->get("device.status", deviceStatus) == 0) { + /* + * There is a slight question as to exactly how the noise + * profile, specifically the constant part of it, scales. For + * now we assume it all scales the same, and we'll revisit this + * if it proves substantially wrong. NOTE: we may also want to + * make some adjustments based on the camera mode (such as + * binning), if we knew how to discover it... + */ + double factor = sqrt(deviceStatus.analogueGain) / modeFactor_; + struct NoiseStatus status; + status.noiseConstant = referenceConstant_ * factor; + status.noiseSlope = referenceSlope_ * factor; + imageMetadata->set("noise.status", status); + LOG(RPiNoise, Debug) + << "constant " << status.noiseConstant + << " slope " << status.noiseSlope; + } else + LOG(RPiNoise, Warning) << " no metadata"; +} + +/* Register algorithm with the system. */ +static Algorithm *create(Controller *controller) +{ + return new Noise(controller); +} +static RegisterAlgorithm reg(NAME, &create); diff --git a/src/ipa/rpi/controller/rpi/noise.h b/src/ipa/rpi/controller/rpi/noise.h new file mode 100644 index 00000000..74c31e64 --- /dev/null +++ b/src/ipa/rpi/controller/rpi/noise.h @@ -0,0 +1,32 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (C) 2019, Raspberry Pi Ltd + * + * noise.h - Noise control algorithm + */ +#pragma once + +#include "../algorithm.h" +#include "../noise_status.h" + +/* This is our implementation of the "noise algorithm". */ + +namespace RPiController { + +class Noise : public Algorithm +{ +public: + Noise(Controller *controller); + char const *name() const override; + void switchMode(CameraMode const &cameraMode, Metadata *metadata) override; + int read(const libcamera::YamlObject ¶ms) override; + void prepare(Metadata *imageMetadata) override; + +private: + /* the noise profile for analogue gain of 1.0 */ + double referenceConstant_; + double referenceSlope_; + double modeFactor_; +}; + +} /* namespace RPiController */ diff --git a/src/ipa/rpi/controller/rpi/sdn.cpp b/src/ipa/rpi/controller/rpi/sdn.cpp new file mode 100644 index 00000000..b6b66251 --- /dev/null +++ b/src/ipa/rpi/controller/rpi/sdn.cpp @@ -0,0 +1,80 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (C) 2019-2021, Raspberry Pi Ltd + * + * sdn.cpp - SDN (spatial denoise) control algorithm + */ + +#include + +#include "../denoise_status.h" +#include "../noise_status.h" + +#include "sdn.h" + +using namespace RPiController; +using namespace libcamera; + +LOG_DEFINE_CATEGORY(RPiSdn) + +/* + * Calculate settings for the spatial denoise block using the noise profile in + * the image metadata. + */ + +#define NAME "rpi.sdn" + +Sdn::Sdn(Controller *controller) + : DenoiseAlgorithm(controller), mode_(DenoiseMode::ColourOff) +{ +} + +char const *Sdn::name() const +{ + return NAME; +} + +int Sdn::read(const libcamera::YamlObject ¶ms) +{ + deviation_ = params["deviation"].get(3.2); + strength_ = params["strength"].get(0.75); + return 0; +} + +void Sdn::initialise() +{ +} + +void Sdn::prepare(Metadata *imageMetadata) +{ + struct NoiseStatus noiseStatus = {}; + noiseStatus.noiseSlope = 3.0; /* in case no metadata */ + if (imageMetadata->get("noise.status", noiseStatus) != 0) + LOG(RPiSdn, Warning) << "no noise profile found"; + LOG(RPiSdn, Debug) + << "Noise profile: constant " << noiseStatus.noiseConstant + << " slope " << noiseStatus.noiseSlope; + struct DenoiseStatus status; + status.noiseConstant = noiseStatus.noiseConstant * deviation_; + status.noiseSlope = noiseStatus.noiseSlope * deviation_; + status.strength = strength_; + status.mode = static_cast>(mode_); + imageMetadata->set("denoise.status", status); + LOG(RPiSdn, Debug) + << "programmed constant " << status.noiseConstant + << " slope " << status.noiseSlope + << " strength " << status.strength; +} + +void Sdn::setMode(DenoiseMode mode) +{ + /* We only distinguish between off and all other modes. */ + mode_ = mode; +} + +/* Register algorithm with the system. */ +static Algorithm *create(Controller *controller) +{ + return (Algorithm *)new Sdn(controller); +} +static RegisterAlgorithm reg(NAME, &create); diff --git a/src/ipa/rpi/controller/rpi/sdn.h b/src/ipa/rpi/controller/rpi/sdn.h new file mode 100644 index 00000000..9dd73c38 --- /dev/null +++ b/src/ipa/rpi/controller/rpi/sdn.h @@ -0,0 +1,32 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (C) 2019, Raspberry Pi Ltd + * + * sdn.h - SDN (spatial denoise) control algorithm + */ +#pragma once + +#include "../algorithm.h" +#include "../denoise_algorithm.h" + +namespace RPiController { + +/* Algorithm to calculate correct spatial denoise (SDN) settings. */ + +class Sdn : public DenoiseAlgorithm +{ +public: + Sdn(Controller *controller = NULL); + char const *name() const override; + int read(const libcamera::YamlObject ¶ms) override; + void initialise() override; + void prepare(Metadata *imageMetadata) override; + void setMode(DenoiseMode mode) override; + +private: + double deviation_; + double strength_; + DenoiseMode mode_; +}; + +} /* namespace RPiController */ diff --git a/src/ipa/rpi/controller/rpi/sharpen.cpp b/src/ipa/rpi/controller/rpi/sharpen.cpp new file mode 100644 index 00000000..4f6f020a --- /dev/null +++ b/src/ipa/rpi/controller/rpi/sharpen.cpp @@ -0,0 +1,92 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (C) 2019, Raspberry Pi Ltd + * + * sharpen.cpp - sharpening control algorithm + */ + +#include + +#include + +#include "../sharpen_status.h" + +#include "sharpen.h" + +using namespace RPiController; +using namespace libcamera; + +LOG_DEFINE_CATEGORY(RPiSharpen) + +#define NAME "rpi.sharpen" + +Sharpen::Sharpen(Controller *controller) + : SharpenAlgorithm(controller), userStrength_(1.0) +{ +} + +char const *Sharpen::name() const +{ + return NAME; +} + +void Sharpen::switchMode(CameraMode const &cameraMode, + [[maybe_unused]] Metadata *metadata) +{ + /* can't be less than one, right? */ + modeFactor_ = std::max(1.0, cameraMode.noiseFactor); +} + +int Sharpen::read(const libcamera::YamlObject ¶ms) +{ + threshold_ = params["threshold"].get(1.0); + strength_ = params["strength"].get(1.0); + limit_ = params["limit"].get(1.0); + LOG(RPiSharpen, Debug) + << "Read threshold " << threshold_ + << " strength " << strength_ + << " limit " << limit_; + return 0; +} + +void Sharpen::setStrength(double strength) +{ + /* + * Note that this function is how an application sets the overall + * sharpening "strength". We call this the "user strength" field + * as there already is a strength_ field - being an internal gain + * parameter that gets passed to the ISP control code. Negative + * values are not allowed - coerce them to zero (no sharpening). + */ + userStrength_ = std::max(0.0, strength); +} + +void Sharpen::prepare(Metadata *imageMetadata) +{ + /* + * The userStrength_ affects the algorithm's internal gain directly, but + * we adjust the limit and threshold less aggressively. Using a sqrt + * function is an arbitrary but gentle way of accomplishing this. + */ + double userStrengthSqrt = sqrt(userStrength_); + struct SharpenStatus status; + /* + * Binned modes seem to need the sharpening toned down with this + * pipeline, thus we use the modeFactor_ here. Also avoid + * divide-by-zero with the userStrengthSqrt. + */ + status.threshold = threshold_ * modeFactor_ / + std::max(0.01, userStrengthSqrt); + status.strength = strength_ / modeFactor_ * userStrength_; + status.limit = limit_ / modeFactor_ * userStrengthSqrt; + /* Finally, report any application-supplied parameters that were used. */ + status.userStrength = userStrength_; + imageMetadata->set("sharpen.status", status); +} + +/* Register algorithm with the system. */ +static Algorithm *create(Controller *controller) +{ + return new Sharpen(controller); +} +static RegisterAlgorithm reg(NAME, &create); diff --git a/src/ipa/rpi/controller/rpi/sharpen.h b/src/ipa/rpi/controller/rpi/sharpen.h new file mode 100644 index 00000000..8bb7631e --- /dev/null +++ b/src/ipa/rpi/controller/rpi/sharpen.h @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (C) 2019, Raspberry Pi Ltd + * + * sharpen.h - sharpening control algorithm + */ +#pragma once + +#include "../sharpen_algorithm.h" +#include "../sharpen_status.h" + +/* This is our implementation of the "sharpen algorithm". */ + +namespace RPiController { + +class Sharpen : public SharpenAlgorithm +{ +public: + Sharpen(Controller *controller); + char const *name() const override; + void switchMode(CameraMode const &cameraMode, Metadata *metadata) override; + int read(const libcamera::YamlObject ¶ms) override; + void setStrength(double strength) override; + void prepare(Metadata *imageMetadata) override; + +private: + double threshold_; + double strength_; + double limit_; + double modeFactor_; + double userStrength_; +}; + +} /* namespace RPiController */ diff --git a/src/ipa/rpi/controller/sharpen_algorithm.h b/src/ipa/rpi/controller/sharpen_algorithm.h new file mode 100644 index 00000000..3be21c32 --- /dev/null +++ b/src/ipa/rpi/controller/sharpen_algorithm.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (C) 2020, Raspberry Pi Ltd + * + * sharpen_algorithm.h - sharpness control algorithm interface + */ +#pragma once + +#include "algorithm.h" + +namespace RPiController { + +class SharpenAlgorithm : public Algorithm +{ +public: + SharpenAlgorithm(Controller *controller) : Algorithm(controller) {} + /* A sharpness control algorithm must provide the following: */ + virtual void setStrength(double strength) = 0; +}; + +} /* namespace RPiController */ diff --git a/src/ipa/rpi/controller/sharpen_status.h b/src/ipa/rpi/controller/sharpen_status.h new file mode 100644 index 00000000..106166db --- /dev/null +++ b/src/ipa/rpi/controller/sharpen_status.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (C) 2019, Raspberry Pi Ltd + * + * sharpen_status.h - Sharpen control algorithm status + */ +#pragma once + +/* The "sharpen" algorithm stores the strength to use. */ + +struct SharpenStatus { + /* controls the smallest level of detail (or noise!) that sharpening will pick up */ + double threshold; + /* the rate at which the sharpening response ramps once above the threshold */ + double strength; + /* upper limit of the allowed sharpening response */ + double limit; + /* The sharpening strength requested by the user or application. */ + double userStrength; +}; diff --git a/src/ipa/rpi/controller/statistics.h b/src/ipa/rpi/controller/statistics.h new file mode 100644 index 00000000..015d4efc --- /dev/null +++ b/src/ipa/rpi/controller/statistics.h @@ -0,0 +1,78 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (C) 2022, Raspberry Pi Ltd + * + * statistics.h - Raspberry Pi generic statistics structure + */ +#pragma once + +#include +#include +#include + +#include "histogram.h" +#include "region_stats.h" + +namespace RPiController { + +struct RgbySums { + RgbySums(uint64_t _rSum = 0, uint64_t _gSum = 0, uint64_t _bSum = 0, uint64_t _ySum = 0) + : rSum(_rSum), gSum(_gSum), bSum(_bSum), ySum(_ySum) + { + } + uint64_t rSum; + uint64_t gSum; + uint64_t bSum; + uint64_t ySum; +}; + +using RgbyRegions = RegionStats; +using FocusRegions = RegionStats; + +struct Statistics { + /* + * All region based statistics are normalised to 16-bits, giving a + * maximum value of (1 << NormalisationFactorPow2) - 1. + */ + static constexpr unsigned int NormalisationFactorPow2 = 16; + + /* + * Positioning of the AGC statistics gathering in the pipeline: + * Pre-WB correction or post-WB correction. + * Assume this is post-LSC. + */ + enum class AgcStatsPos { PreWb, PostWb }; + const AgcStatsPos agcStatsPos; + + /* + * Positioning of the AWB/ALSC statistics gathering in the pipeline: + * Pre-LSC or post-LSC. + */ + enum class ColourStatsPos { PreLsc, PostLsc }; + const ColourStatsPos colourStatsPos; + + Statistics(AgcStatsPos a, ColourStatsPos c) + : agcStatsPos(a), colourStatsPos(c) + { + } + + /* Histogram statistics. Not all histograms may be populated! */ + Histogram rHist; + Histogram gHist; + Histogram bHist; + Histogram yHist; + + /* Row sums for flicker avoidance. */ + std::vector rowSums; + + /* Region based colour sums. */ + RgbyRegions agcRegions; + RgbyRegions awbRegions; + + /* Region based focus FoM. */ + FocusRegions focusRegions; +}; + +using StatisticsPtr = std::shared_ptr; + +} /* namespace RPiController */ diff --git a/src/ipa/rpi/meson.build b/src/ipa/rpi/meson.build new file mode 100644 index 00000000..7d7a61f7 --- /dev/null +++ b/src/ipa/rpi/meson.build @@ -0,0 +1,13 @@ +# SPDX-License-Identifier: CC0-1.0 + +subdir('cam_helper') +subdir('controller') + +foreach pipeline : pipelines + pipeline = pipeline.split('/') + if pipeline.length() < 2 or pipeline[0] != 'rpi' + continue + endif + + subdir(pipeline[1]) +endforeach diff --git a/src/ipa/rpi/vc4/data/imx219.json b/src/ipa/rpi/vc4/data/imx219.json new file mode 100644 index 00000000..efe7210a --- /dev/null +++ b/src/ipa/rpi/vc4/data/imx219.json @@ -0,0 +1,486 @@ +{ + "version": 2.0, + "target": "bcm2835", + "algorithms": [ + { + "rpi.black_level": + { + "black_level": 4096 + } + }, + { + "rpi.dpc": { } + }, + { + "rpi.lux": + { + "reference_shutter_speed": 27685, + "reference_gain": 1.0, + "reference_aperture": 1.0, + "reference_lux": 998, + "reference_Y": 12744 + } + }, + { + "rpi.noise": + { + "reference_constant": 0, + "reference_slope": 3.67 + } + }, + { + "rpi.geq": + { + "offset": 204, + "slope": 0.01633 + } + }, + { + "rpi.sdn": { } + }, + { + "rpi.awb": + { + "priors": [ + { + "lux": 0, + "prior": + [ + 2000, 1.0, + 3000, 0.0, + 13000, 0.0 + ] + }, + { + "lux": 800, + "prior": + [ + 2000, 0.0, + 6000, 2.0, + 13000, 2.0 + ] + }, + { + "lux": 1500, + "prior": + [ + 2000, 0.0, + 4000, 1.0, + 6000, 6.0, + 6500, 7.0, + 7000, 1.0, + 13000, 1.0 + ] + } + ], + "modes": + { + "auto": + { + "lo": 2500, + "hi": 8000 + }, + "incandescent": + { + "lo": 2500, + "hi": 3000 + }, + "tungsten": + { + "lo": 3000, + "hi": 3500 + }, + "fluorescent": + { + "lo": 4000, + "hi": 4700 + }, + "indoor": + { + "lo": 3000, + "hi": 5000 + }, + "daylight": + { + "lo": 5500, + "hi": 6500 + }, + "cloudy": + { + "lo": 7000, + "hi": 8600 + } + }, + "bayes": 1, + "ct_curve": + [ + 2498.0, 0.9309, 0.3599, + 2911.0, 0.8682, 0.4283, + 2919.0, 0.8358, 0.4621, + 3627.0, 0.7646, 0.5327, + 4600.0, 0.6079, 0.6721, + 5716.0, 0.5712, 0.7017, + 8575.0, 0.4331, 0.8037 + ], + "sensitivity_r": 1.05, + "sensitivity_b": 1.05, + "transverse_pos": 0.04791, + "transverse_neg": 0.04881 + } + }, + { + "rpi.agc": + { + "metering_modes": + { + "centre-weighted": + { + "weights": [ 3, 3, 3, 2, 2, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0 ] + }, + "spot": + { + "weights": [ 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ] + }, + "matrix": + { + "weights": [ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ] + } + }, + "exposure_modes": + { + "normal": + { + "shutter": [ 100, 10000, 30000, 60000, 66666 ], + "gain": [ 1.0, 2.0, 4.0, 6.0, 8.0 ] + }, + "short": + { + "shutter": [ 100, 5000, 10000, 20000, 33333 ], + "gain": [ 1.0, 2.0, 4.0, 6.0, 8.0 ] + }, + "long": + { + "shutter": [ 100, 10000, 30000, 60000, 120000 ], + "gain": [ 1.0, 2.0, 4.0, 6.0, 12.0 ] + } + }, + "constraint_modes": + { + "normal": [ + { + "bound": "LOWER", + "q_lo": 0.98, + "q_hi": 1.0, + "y_target": + [ + 0, 0.5, + 1000, 0.5 + ] + } + ], + "highlight": [ + { + "bound": "LOWER", + "q_lo": 0.98, + "q_hi": 1.0, + "y_target": + [ + 0, 0.5, + 1000, 0.5 + ] + }, + { + "bound": "UPPER", + "q_lo": 0.98, + "q_hi": 1.0, + "y_target": + [ + 0, 0.8, + 1000, 0.8 + ] + } + ], + "shadows": [ + { + "bound": "LOWER", + "q_lo": 0.0, + "q_hi": 0.5, + "y_target": + [ + 0, 0.17, + 1000, 0.17 + ] + } + ] + }, + "y_target": + [ + 0, 0.16, + 1000, 0.165, + 10000, 0.17 + ] + } + }, + { + "rpi.alsc": + { + "omega": 1.3, + "n_iter": 100, + "luminance_strength": 0.7, + "calibrations_Cr": [ + { + "ct": 3000, + "table": + [ + 1.487, 1.481, 1.481, 1.445, 1.389, 1.327, 1.307, 1.307, 1.307, 1.309, 1.341, 1.405, 1.458, 1.494, 1.494, 1.497, + 1.491, 1.481, 1.448, 1.397, 1.331, 1.275, 1.243, 1.229, 1.229, 1.249, 1.287, 1.349, 1.409, 1.463, 1.494, 1.497, + 1.491, 1.469, 1.405, 1.331, 1.275, 1.217, 1.183, 1.172, 1.172, 1.191, 1.231, 1.287, 1.349, 1.424, 1.484, 1.499, + 1.487, 1.444, 1.363, 1.283, 1.217, 1.183, 1.148, 1.138, 1.138, 1.159, 1.191, 1.231, 1.302, 1.385, 1.461, 1.492, + 1.481, 1.423, 1.334, 1.253, 1.189, 1.148, 1.135, 1.119, 1.123, 1.137, 1.159, 1.203, 1.272, 1.358, 1.442, 1.488, + 1.479, 1.413, 1.321, 1.236, 1.176, 1.139, 1.118, 1.114, 1.116, 1.123, 1.149, 1.192, 1.258, 1.344, 1.432, 1.487, + 1.479, 1.413, 1.321, 1.236, 1.176, 1.139, 1.116, 1.114, 1.115, 1.123, 1.149, 1.192, 1.258, 1.344, 1.432, 1.487, + 1.479, 1.425, 1.336, 1.251, 1.189, 1.149, 1.136, 1.118, 1.121, 1.138, 1.158, 1.206, 1.275, 1.358, 1.443, 1.488, + 1.488, 1.448, 1.368, 1.285, 1.219, 1.189, 1.149, 1.139, 1.139, 1.158, 1.195, 1.235, 1.307, 1.387, 1.462, 1.493, + 1.496, 1.475, 1.411, 1.337, 1.284, 1.219, 1.189, 1.176, 1.176, 1.195, 1.235, 1.296, 1.356, 1.429, 1.487, 1.501, + 1.495, 1.489, 1.458, 1.407, 1.337, 1.287, 1.253, 1.239, 1.239, 1.259, 1.296, 1.356, 1.419, 1.472, 1.499, 1.499, + 1.494, 1.489, 1.489, 1.453, 1.398, 1.336, 1.317, 1.317, 1.317, 1.321, 1.351, 1.416, 1.467, 1.501, 1.501, 1.499 + ] + }, + { + "ct": 3850, + "table": + [ + 1.694, 1.688, 1.688, 1.649, 1.588, 1.518, 1.495, 1.495, 1.495, 1.497, 1.532, 1.602, 1.659, 1.698, 1.698, 1.703, + 1.698, 1.688, 1.653, 1.597, 1.525, 1.464, 1.429, 1.413, 1.413, 1.437, 1.476, 1.542, 1.606, 1.665, 1.698, 1.703, + 1.697, 1.673, 1.605, 1.525, 1.464, 1.401, 1.369, 1.354, 1.354, 1.377, 1.417, 1.476, 1.542, 1.623, 1.687, 1.705, + 1.692, 1.646, 1.561, 1.472, 1.401, 1.368, 1.337, 1.323, 1.324, 1.348, 1.377, 1.417, 1.492, 1.583, 1.661, 1.697, + 1.686, 1.625, 1.528, 1.439, 1.372, 1.337, 1.321, 1.311, 1.316, 1.324, 1.348, 1.389, 1.461, 1.553, 1.642, 1.694, + 1.684, 1.613, 1.514, 1.423, 1.359, 1.328, 1.311, 1.306, 1.306, 1.316, 1.339, 1.378, 1.446, 1.541, 1.633, 1.693, + 1.684, 1.613, 1.514, 1.423, 1.359, 1.328, 1.311, 1.305, 1.305, 1.316, 1.339, 1.378, 1.446, 1.541, 1.633, 1.693, + 1.685, 1.624, 1.529, 1.438, 1.372, 1.336, 1.324, 1.309, 1.314, 1.323, 1.348, 1.392, 1.462, 1.555, 1.646, 1.694, + 1.692, 1.648, 1.561, 1.473, 1.403, 1.372, 1.336, 1.324, 1.324, 1.348, 1.378, 1.423, 1.495, 1.585, 1.667, 1.701, + 1.701, 1.677, 1.608, 1.527, 1.471, 1.403, 1.375, 1.359, 1.359, 1.378, 1.423, 1.488, 1.549, 1.631, 1.694, 1.709, + 1.702, 1.694, 1.656, 1.601, 1.527, 1.473, 1.441, 1.424, 1.424, 1.443, 1.488, 1.549, 1.621, 1.678, 1.706, 1.707, + 1.699, 1.694, 1.694, 1.654, 1.593, 1.525, 1.508, 1.508, 1.508, 1.509, 1.546, 1.614, 1.674, 1.708, 1.708, 1.707 + ] + }, + { + "ct": 6000, + "table": + [ + 2.179, 2.176, 2.176, 2.125, 2.048, 1.975, 1.955, 1.954, 1.954, 1.956, 1.993, 2.071, 2.141, 2.184, 2.185, 2.188, + 2.189, 2.176, 2.128, 2.063, 1.973, 1.908, 1.872, 1.856, 1.856, 1.876, 1.922, 1.999, 2.081, 2.144, 2.184, 2.192, + 2.187, 2.152, 2.068, 1.973, 1.907, 1.831, 1.797, 1.786, 1.786, 1.804, 1.853, 1.922, 1.999, 2.089, 2.166, 2.191, + 2.173, 2.117, 2.013, 1.908, 1.831, 1.791, 1.755, 1.749, 1.749, 1.767, 1.804, 1.853, 1.939, 2.041, 2.135, 2.181, + 2.166, 2.089, 1.975, 1.869, 1.792, 1.755, 1.741, 1.731, 1.734, 1.749, 1.767, 1.818, 1.903, 2.005, 2.111, 2.173, + 2.165, 2.074, 1.956, 1.849, 1.777, 1.742, 1.729, 1.725, 1.729, 1.734, 1.758, 1.804, 1.884, 1.991, 2.099, 2.172, + 2.165, 2.074, 1.956, 1.849, 1.777, 1.742, 1.727, 1.724, 1.725, 1.734, 1.758, 1.804, 1.884, 1.991, 2.099, 2.172, + 2.166, 2.085, 1.975, 1.869, 1.791, 1.755, 1.741, 1.729, 1.733, 1.749, 1.769, 1.819, 1.904, 2.009, 2.114, 2.174, + 2.174, 2.118, 2.015, 1.913, 1.831, 1.791, 1.755, 1.749, 1.749, 1.769, 1.811, 1.855, 1.943, 2.047, 2.139, 2.183, + 2.187, 2.151, 2.072, 1.979, 1.911, 1.831, 1.801, 1.791, 1.791, 1.811, 1.855, 1.933, 2.006, 2.101, 2.173, 2.197, + 2.189, 2.178, 2.132, 2.069, 1.979, 1.913, 1.879, 1.867, 1.867, 1.891, 1.933, 2.006, 2.091, 2.156, 2.195, 2.197, + 2.181, 2.179, 2.178, 2.131, 2.057, 1.981, 1.965, 1.965, 1.965, 1.969, 1.999, 2.083, 2.153, 2.197, 2.197, 2.196 + ] + } + ], + "calibrations_Cb": [ + { + "ct": 3000, + "table": + [ + 1.967, 1.961, 1.955, 1.953, 1.954, 1.957, 1.961, 1.963, 1.963, 1.961, 1.959, 1.957, 1.954, 1.951, 1.951, 1.955, + 1.961, 1.959, 1.957, 1.956, 1.962, 1.967, 1.975, 1.979, 1.979, 1.975, 1.971, 1.967, 1.957, 1.952, 1.951, 1.951, + 1.959, 1.959, 1.959, 1.966, 1.976, 1.989, 1.999, 2.004, 2.003, 1.997, 1.991, 1.981, 1.967, 1.956, 1.951, 1.951, + 1.959, 1.962, 1.967, 1.978, 1.993, 2.009, 2.021, 2.028, 2.026, 2.021, 2.011, 1.995, 1.981, 1.964, 1.953, 1.951, + 1.961, 1.965, 1.977, 1.993, 2.009, 2.023, 2.041, 2.047, 2.047, 2.037, 2.024, 2.011, 1.995, 1.975, 1.958, 1.953, + 1.963, 1.968, 1.981, 2.001, 2.019, 2.039, 2.046, 2.052, 2.052, 2.051, 2.035, 2.021, 2.001, 1.978, 1.959, 1.955, + 1.961, 1.966, 1.981, 2.001, 2.019, 2.038, 2.043, 2.051, 2.052, 2.042, 2.034, 2.019, 2.001, 1.978, 1.959, 1.954, + 1.957, 1.961, 1.972, 1.989, 2.003, 2.021, 2.038, 2.039, 2.039, 2.034, 2.019, 2.004, 1.988, 1.971, 1.954, 1.949, + 1.952, 1.953, 1.959, 1.972, 1.989, 2.003, 2.016, 2.019, 2.019, 2.014, 2.003, 1.988, 1.971, 1.955, 1.948, 1.947, + 1.949, 1.948, 1.949, 1.957, 1.971, 1.978, 1.991, 1.994, 1.994, 1.989, 1.979, 1.967, 1.954, 1.946, 1.947, 1.947, + 1.949, 1.946, 1.944, 1.946, 1.949, 1.954, 1.962, 1.967, 1.967, 1.963, 1.956, 1.948, 1.943, 1.943, 1.946, 1.949, + 1.951, 1.946, 1.944, 1.942, 1.943, 1.943, 1.947, 1.948, 1.949, 1.947, 1.945, 1.941, 1.938, 1.939, 1.948, 1.952 + ] + }, + { + "ct": 3850, + "table": + [ + 1.726, 1.724, 1.722, 1.723, 1.731, 1.735, 1.743, 1.746, 1.746, 1.741, 1.735, 1.729, 1.725, 1.721, 1.721, 1.721, + 1.724, 1.723, 1.723, 1.727, 1.735, 1.744, 1.749, 1.756, 1.756, 1.749, 1.744, 1.735, 1.727, 1.719, 1.719, 1.719, + 1.723, 1.723, 1.724, 1.735, 1.746, 1.759, 1.767, 1.775, 1.775, 1.766, 1.758, 1.746, 1.735, 1.723, 1.718, 1.716, + 1.723, 1.725, 1.732, 1.746, 1.759, 1.775, 1.782, 1.792, 1.792, 1.782, 1.772, 1.759, 1.745, 1.729, 1.718, 1.716, + 1.725, 1.729, 1.738, 1.756, 1.775, 1.785, 1.796, 1.803, 1.804, 1.794, 1.783, 1.772, 1.757, 1.736, 1.722, 1.718, + 1.728, 1.731, 1.741, 1.759, 1.781, 1.795, 1.803, 1.806, 1.808, 1.805, 1.791, 1.779, 1.762, 1.739, 1.722, 1.721, + 1.727, 1.731, 1.741, 1.759, 1.781, 1.791, 1.799, 1.804, 1.806, 1.801, 1.791, 1.779, 1.762, 1.739, 1.722, 1.717, + 1.722, 1.724, 1.733, 1.751, 1.768, 1.781, 1.791, 1.796, 1.799, 1.791, 1.781, 1.766, 1.754, 1.731, 1.717, 1.714, + 1.718, 1.718, 1.724, 1.737, 1.752, 1.768, 1.776, 1.782, 1.784, 1.781, 1.766, 1.754, 1.737, 1.724, 1.713, 1.709, + 1.716, 1.715, 1.716, 1.725, 1.737, 1.749, 1.756, 1.763, 1.764, 1.762, 1.749, 1.737, 1.724, 1.717, 1.709, 1.708, + 1.715, 1.714, 1.712, 1.715, 1.722, 1.729, 1.736, 1.741, 1.742, 1.739, 1.731, 1.723, 1.717, 1.712, 1.711, 1.709, + 1.716, 1.714, 1.711, 1.712, 1.715, 1.719, 1.723, 1.728, 1.731, 1.729, 1.723, 1.718, 1.711, 1.711, 1.713, 1.713 + ] + }, + { + "ct": 6000, + "table": + [ + 1.374, 1.372, 1.373, 1.374, 1.375, 1.378, 1.378, 1.381, 1.382, 1.382, 1.378, 1.373, 1.372, 1.369, 1.365, 1.365, + 1.371, 1.371, 1.372, 1.374, 1.378, 1.381, 1.384, 1.386, 1.388, 1.387, 1.384, 1.377, 1.372, 1.368, 1.364, 1.362, + 1.369, 1.371, 1.372, 1.377, 1.383, 1.391, 1.394, 1.396, 1.397, 1.395, 1.391, 1.382, 1.374, 1.369, 1.362, 1.361, + 1.369, 1.371, 1.375, 1.383, 1.391, 1.399, 1.402, 1.404, 1.405, 1.403, 1.398, 1.391, 1.379, 1.371, 1.363, 1.361, + 1.371, 1.373, 1.378, 1.388, 1.399, 1.407, 1.411, 1.413, 1.413, 1.411, 1.405, 1.397, 1.385, 1.374, 1.366, 1.362, + 1.371, 1.374, 1.379, 1.389, 1.405, 1.411, 1.414, 1.414, 1.415, 1.415, 1.411, 1.401, 1.388, 1.376, 1.367, 1.363, + 1.371, 1.373, 1.379, 1.389, 1.405, 1.408, 1.413, 1.414, 1.414, 1.413, 1.409, 1.401, 1.388, 1.376, 1.367, 1.362, + 1.366, 1.369, 1.374, 1.384, 1.396, 1.404, 1.407, 1.408, 1.408, 1.408, 1.401, 1.395, 1.382, 1.371, 1.363, 1.359, + 1.364, 1.365, 1.368, 1.375, 1.386, 1.396, 1.399, 1.401, 1.399, 1.399, 1.395, 1.385, 1.374, 1.365, 1.359, 1.357, + 1.361, 1.363, 1.365, 1.368, 1.377, 1.384, 1.388, 1.391, 1.391, 1.388, 1.385, 1.375, 1.366, 1.361, 1.358, 1.356, + 1.361, 1.362, 1.362, 1.364, 1.367, 1.373, 1.376, 1.377, 1.377, 1.375, 1.373, 1.366, 1.362, 1.358, 1.358, 1.358, + 1.361, 1.362, 1.362, 1.362, 1.363, 1.367, 1.369, 1.368, 1.367, 1.367, 1.367, 1.364, 1.358, 1.357, 1.358, 1.359 + ] + } + ], + "luminance_lut": + [ + 2.716, 2.568, 2.299, 2.065, 1.845, 1.693, 1.605, 1.597, 1.596, 1.634, 1.738, 1.914, 2.145, 2.394, 2.719, 2.901, + 2.593, 2.357, 2.093, 1.876, 1.672, 1.528, 1.438, 1.393, 1.394, 1.459, 1.569, 1.731, 1.948, 2.169, 2.481, 2.756, + 2.439, 2.197, 1.922, 1.691, 1.521, 1.365, 1.266, 1.222, 1.224, 1.286, 1.395, 1.573, 1.747, 1.988, 2.299, 2.563, + 2.363, 2.081, 1.797, 1.563, 1.376, 1.244, 1.152, 1.099, 1.101, 1.158, 1.276, 1.421, 1.607, 1.851, 2.163, 2.455, + 2.342, 2.003, 1.715, 1.477, 1.282, 1.152, 1.074, 1.033, 1.035, 1.083, 1.163, 1.319, 1.516, 1.759, 2.064, 2.398, + 2.342, 1.985, 1.691, 1.446, 1.249, 1.111, 1.034, 1.004, 1.004, 1.028, 1.114, 1.274, 1.472, 1.716, 2.019, 2.389, + 2.342, 1.991, 1.691, 1.446, 1.249, 1.112, 1.034, 1.011, 1.005, 1.035, 1.114, 1.274, 1.472, 1.716, 2.019, 2.389, + 2.365, 2.052, 1.751, 1.499, 1.299, 1.171, 1.089, 1.039, 1.042, 1.084, 1.162, 1.312, 1.516, 1.761, 2.059, 2.393, + 2.434, 2.159, 1.856, 1.601, 1.403, 1.278, 1.166, 1.114, 1.114, 1.162, 1.266, 1.402, 1.608, 1.847, 2.146, 2.435, + 2.554, 2.306, 2.002, 1.748, 1.563, 1.396, 1.299, 1.247, 1.243, 1.279, 1.386, 1.551, 1.746, 1.977, 2.272, 2.518, + 2.756, 2.493, 2.195, 1.947, 1.739, 1.574, 1.481, 1.429, 1.421, 1.457, 1.559, 1.704, 1.929, 2.159, 2.442, 2.681, + 2.935, 2.739, 2.411, 2.151, 1.922, 1.749, 1.663, 1.628, 1.625, 1.635, 1.716, 1.872, 2.113, 2.368, 2.663, 2.824 + ], + "sigma": 0.00381, + "sigma_Cb": 0.00216 + } + }, + { + "rpi.contrast": + { + "ce_enable": 1, + "gamma_curve": + [ + 0, 0, + 1024, 5040, + 2048, 9338, + 3072, 12356, + 4096, 15312, + 5120, 18051, + 6144, 20790, + 7168, 23193, + 8192, 25744, + 9216, 27942, + 10240, 30035, + 11264, 32005, + 12288, 33975, + 13312, 35815, + 14336, 37600, + 15360, 39168, + 16384, 40642, + 18432, 43379, + 20480, 45749, + 22528, 47753, + 24576, 49621, + 26624, 51253, + 28672, 52698, + 30720, 53796, + 32768, 54876, + 36864, 57012, + 40960, 58656, + 45056, 59954, + 49152, 61183, + 53248, 62355, + 57344, 63419, + 61440, 64476, + 65535, 65535 + ] + } + }, + { + "rpi.ccm": + { + "ccms": [ + { + "ct": 2498, + "ccm": + [ + 1.58731, -0.18011, -0.40721, + -0.60639, 2.03422, -0.42782, + -0.19612, -1.69203, 2.88815 + ] + }, + { + "ct": 2811, + "ccm": + [ + 1.61593, -0.33164, -0.28429, + -0.55048, 1.97779, -0.42731, + -0.12042, -1.42847, 2.54889 + ] + }, + { + "ct": 2911, + "ccm": + [ + 1.62771, -0.41282, -0.21489, + -0.57991, 2.04176, -0.46186, + -0.07613, -1.13359, 2.20972 + ] + }, + { + "ct": 2919, + "ccm": + [ + 1.62661, -0.37736, -0.24925, + -0.52519, 1.95233, -0.42714, + -0.10842, -1.34929, 2.45771 + ] + }, + { + "ct": 3627, + "ccm": + [ + 1.70385, -0.57231, -0.13154, + -0.47763, 1.85998, -0.38235, + -0.07467, -0.82678, 1.90145 + ] + }, + { + "ct": 4600, + "ccm": + [ + 1.68486, -0.61085, -0.07402, + -0.41927, 2.04016, -0.62089, + -0.08633, -0.67672, 1.76305 + ] + }, + { + "ct": 5716, + "ccm": + [ + 1.80439, -0.73699, -0.06739, + -0.36073, 1.83327, -0.47255, + -0.08378, -0.56403, 1.64781 + ] + }, + { + "ct": 8575, + "ccm": + [ + 1.89357, -0.76427, -0.12931, + -0.27399, 2.15605, -0.88206, + -0.12035, -0.68256, 1.80292 + ] + } + ] + } + }, + { + "rpi.sharpen": { } + } + ] +} \ No newline at end of file diff --git a/src/ipa/rpi/vc4/data/imx219_noir.json b/src/ipa/rpi/vc4/data/imx219_noir.json new file mode 100644 index 00000000..cfedb943 --- /dev/null +++ b/src/ipa/rpi/vc4/data/imx219_noir.json @@ -0,0 +1,402 @@ +{ + "version": 2.0, + "target": "bcm2835", + "algorithms": [ + { + "rpi.black_level": + { + "black_level": 4096 + } + }, + { + "rpi.dpc": { } + }, + { + "rpi.lux": + { + "reference_shutter_speed": 27685, + "reference_gain": 1.0, + "reference_aperture": 1.0, + "reference_lux": 998, + "reference_Y": 12744 + } + }, + { + "rpi.noise": + { + "reference_constant": 0, + "reference_slope": 3.67 + } + }, + { + "rpi.geq": + { + "offset": 204, + "slope": 0.01633 + } + }, + { + "rpi.sdn": { } + }, + { + "rpi.awb": + { + "bayes": 0 + } + }, + { + "rpi.agc": + { + "metering_modes": + { + "centre-weighted": + { + "weights": [ 3, 3, 3, 2, 2, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0 ] + }, + "spot": + { + "weights": [ 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ] + }, + "matrix": + { + "weights": [ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ] + } + }, + "exposure_modes": + { + "normal": + { + "shutter": [ 100, 10000, 30000, 60000, 66666 ], + "gain": [ 1.0, 2.0, 4.0, 6.0, 8.0 ] + }, + "short": + { + "shutter": [ 100, 5000, 10000, 20000, 33333 ], + "gain": [ 1.0, 2.0, 4.0, 6.0, 8.0 ] + }, + "long": + { + "shutter": [ 100, 10000, 30000, 60000, 120000 ], + "gain": [ 1.0, 2.0, 4.0, 6.0, 12.0 ] + } + }, + "constraint_modes": + { + "normal": [ + { + "bound": "LOWER", + "q_lo": 0.98, + "q_hi": 1.0, + "y_target": + [ + 0, 0.5, + 1000, 0.5 + ] + } + ], + "highlight": [ + { + "bound": "LOWER", + "q_lo": 0.98, + "q_hi": 1.0, + "y_target": + [ + 0, 0.5, + 1000, 0.5 + ] + }, + { + "bound": "UPPER", + "q_lo": 0.98, + "q_hi": 1.0, + "y_target": + [ + 0, 0.8, + 1000, 0.8 + ] + } + ], + "shadows": [ + { + "bound": "LOWER", + "q_lo": 0.0, + "q_hi": 0.5, + "y_target": + [ + 0, 0.17, + 1000, 0.17 + ] + } + ] + }, + "y_target": + [ + 0, 0.16, + 1000, 0.165, + 10000, 0.17 + ] + } + }, + { + "rpi.alsc": + { + "omega": 1.3, + "n_iter": 100, + "luminance_strength": 0.7, + "calibrations_Cr": [ + { + "ct": 3000, + "table": + [ + 1.487, 1.481, 1.481, 1.445, 1.389, 1.327, 1.307, 1.307, 1.307, 1.309, 1.341, 1.405, 1.458, 1.494, 1.494, 1.497, + 1.491, 1.481, 1.448, 1.397, 1.331, 1.275, 1.243, 1.229, 1.229, 1.249, 1.287, 1.349, 1.409, 1.463, 1.494, 1.497, + 1.491, 1.469, 1.405, 1.331, 1.275, 1.217, 1.183, 1.172, 1.172, 1.191, 1.231, 1.287, 1.349, 1.424, 1.484, 1.499, + 1.487, 1.444, 1.363, 1.283, 1.217, 1.183, 1.148, 1.138, 1.138, 1.159, 1.191, 1.231, 1.302, 1.385, 1.461, 1.492, + 1.481, 1.423, 1.334, 1.253, 1.189, 1.148, 1.135, 1.119, 1.123, 1.137, 1.159, 1.203, 1.272, 1.358, 1.442, 1.488, + 1.479, 1.413, 1.321, 1.236, 1.176, 1.139, 1.118, 1.114, 1.116, 1.123, 1.149, 1.192, 1.258, 1.344, 1.432, 1.487, + 1.479, 1.413, 1.321, 1.236, 1.176, 1.139, 1.116, 1.114, 1.115, 1.123, 1.149, 1.192, 1.258, 1.344, 1.432, 1.487, + 1.479, 1.425, 1.336, 1.251, 1.189, 1.149, 1.136, 1.118, 1.121, 1.138, 1.158, 1.206, 1.275, 1.358, 1.443, 1.488, + 1.488, 1.448, 1.368, 1.285, 1.219, 1.189, 1.149, 1.139, 1.139, 1.158, 1.195, 1.235, 1.307, 1.387, 1.462, 1.493, + 1.496, 1.475, 1.411, 1.337, 1.284, 1.219, 1.189, 1.176, 1.176, 1.195, 1.235, 1.296, 1.356, 1.429, 1.487, 1.501, + 1.495, 1.489, 1.458, 1.407, 1.337, 1.287, 1.253, 1.239, 1.239, 1.259, 1.296, 1.356, 1.419, 1.472, 1.499, 1.499, + 1.494, 1.489, 1.489, 1.453, 1.398, 1.336, 1.317, 1.317, 1.317, 1.321, 1.351, 1.416, 1.467, 1.501, 1.501, 1.499 + ] + }, + { + "ct": 3850, + "table": + [ + 1.694, 1.688, 1.688, 1.649, 1.588, 1.518, 1.495, 1.495, 1.495, 1.497, 1.532, 1.602, 1.659, 1.698, 1.698, 1.703, + 1.698, 1.688, 1.653, 1.597, 1.525, 1.464, 1.429, 1.413, 1.413, 1.437, 1.476, 1.542, 1.606, 1.665, 1.698, 1.703, + 1.697, 1.673, 1.605, 1.525, 1.464, 1.401, 1.369, 1.354, 1.354, 1.377, 1.417, 1.476, 1.542, 1.623, 1.687, 1.705, + 1.692, 1.646, 1.561, 1.472, 1.401, 1.368, 1.337, 1.323, 1.324, 1.348, 1.377, 1.417, 1.492, 1.583, 1.661, 1.697, + 1.686, 1.625, 1.528, 1.439, 1.372, 1.337, 1.321, 1.311, 1.316, 1.324, 1.348, 1.389, 1.461, 1.553, 1.642, 1.694, + 1.684, 1.613, 1.514, 1.423, 1.359, 1.328, 1.311, 1.306, 1.306, 1.316, 1.339, 1.378, 1.446, 1.541, 1.633, 1.693, + 1.684, 1.613, 1.514, 1.423, 1.359, 1.328, 1.311, 1.305, 1.305, 1.316, 1.339, 1.378, 1.446, 1.541, 1.633, 1.693, + 1.685, 1.624, 1.529, 1.438, 1.372, 1.336, 1.324, 1.309, 1.314, 1.323, 1.348, 1.392, 1.462, 1.555, 1.646, 1.694, + 1.692, 1.648, 1.561, 1.473, 1.403, 1.372, 1.336, 1.324, 1.324, 1.348, 1.378, 1.423, 1.495, 1.585, 1.667, 1.701, + 1.701, 1.677, 1.608, 1.527, 1.471, 1.403, 1.375, 1.359, 1.359, 1.378, 1.423, 1.488, 1.549, 1.631, 1.694, 1.709, + 1.702, 1.694, 1.656, 1.601, 1.527, 1.473, 1.441, 1.424, 1.424, 1.443, 1.488, 1.549, 1.621, 1.678, 1.706, 1.707, + 1.699, 1.694, 1.694, 1.654, 1.593, 1.525, 1.508, 1.508, 1.508, 1.509, 1.546, 1.614, 1.674, 1.708, 1.708, 1.707 + ] + }, + { + "ct": 6000, + "table": + [ + 2.179, 2.176, 2.176, 2.125, 2.048, 1.975, 1.955, 1.954, 1.954, 1.956, 1.993, 2.071, 2.141, 2.184, 2.185, 2.188, + 2.189, 2.176, 2.128, 2.063, 1.973, 1.908, 1.872, 1.856, 1.856, 1.876, 1.922, 1.999, 2.081, 2.144, 2.184, 2.192, + 2.187, 2.152, 2.068, 1.973, 1.907, 1.831, 1.797, 1.786, 1.786, 1.804, 1.853, 1.922, 1.999, 2.089, 2.166, 2.191, + 2.173, 2.117, 2.013, 1.908, 1.831, 1.791, 1.755, 1.749, 1.749, 1.767, 1.804, 1.853, 1.939, 2.041, 2.135, 2.181, + 2.166, 2.089, 1.975, 1.869, 1.792, 1.755, 1.741, 1.731, 1.734, 1.749, 1.767, 1.818, 1.903, 2.005, 2.111, 2.173, + 2.165, 2.074, 1.956, 1.849, 1.777, 1.742, 1.729, 1.725, 1.729, 1.734, 1.758, 1.804, 1.884, 1.991, 2.099, 2.172, + 2.165, 2.074, 1.956, 1.849, 1.777, 1.742, 1.727, 1.724, 1.725, 1.734, 1.758, 1.804, 1.884, 1.991, 2.099, 2.172, + 2.166, 2.085, 1.975, 1.869, 1.791, 1.755, 1.741, 1.729, 1.733, 1.749, 1.769, 1.819, 1.904, 2.009, 2.114, 2.174, + 2.174, 2.118, 2.015, 1.913, 1.831, 1.791, 1.755, 1.749, 1.749, 1.769, 1.811, 1.855, 1.943, 2.047, 2.139, 2.183, + 2.187, 2.151, 2.072, 1.979, 1.911, 1.831, 1.801, 1.791, 1.791, 1.811, 1.855, 1.933, 2.006, 2.101, 2.173, 2.197, + 2.189, 2.178, 2.132, 2.069, 1.979, 1.913, 1.879, 1.867, 1.867, 1.891, 1.933, 2.006, 2.091, 2.156, 2.195, 2.197, + 2.181, 2.179, 2.178, 2.131, 2.057, 1.981, 1.965, 1.965, 1.965, 1.969, 1.999, 2.083, 2.153, 2.197, 2.197, 2.196 + ] + } + ], + "calibrations_Cb": [ + { + "ct": 3000, + "table": + [ + 1.967, 1.961, 1.955, 1.953, 1.954, 1.957, 1.961, 1.963, 1.963, 1.961, 1.959, 1.957, 1.954, 1.951, 1.951, 1.955, + 1.961, 1.959, 1.957, 1.956, 1.962, 1.967, 1.975, 1.979, 1.979, 1.975, 1.971, 1.967, 1.957, 1.952, 1.951, 1.951, + 1.959, 1.959, 1.959, 1.966, 1.976, 1.989, 1.999, 2.004, 2.003, 1.997, 1.991, 1.981, 1.967, 1.956, 1.951, 1.951, + 1.959, 1.962, 1.967, 1.978, 1.993, 2.009, 2.021, 2.028, 2.026, 2.021, 2.011, 1.995, 1.981, 1.964, 1.953, 1.951, + 1.961, 1.965, 1.977, 1.993, 2.009, 2.023, 2.041, 2.047, 2.047, 2.037, 2.024, 2.011, 1.995, 1.975, 1.958, 1.953, + 1.963, 1.968, 1.981, 2.001, 2.019, 2.039, 2.046, 2.052, 2.052, 2.051, 2.035, 2.021, 2.001, 1.978, 1.959, 1.955, + 1.961, 1.966, 1.981, 2.001, 2.019, 2.038, 2.043, 2.051, 2.052, 2.042, 2.034, 2.019, 2.001, 1.978, 1.959, 1.954, + 1.957, 1.961, 1.972, 1.989, 2.003, 2.021, 2.038, 2.039, 2.039, 2.034, 2.019, 2.004, 1.988, 1.971, 1.954, 1.949, + 1.952, 1.953, 1.959, 1.972, 1.989, 2.003, 2.016, 2.019, 2.019, 2.014, 2.003, 1.988, 1.971, 1.955, 1.948, 1.947, + 1.949, 1.948, 1.949, 1.957, 1.971, 1.978, 1.991, 1.994, 1.994, 1.989, 1.979, 1.967, 1.954, 1.946, 1.947, 1.947, + 1.949, 1.946, 1.944, 1.946, 1.949, 1.954, 1.962, 1.967, 1.967, 1.963, 1.956, 1.948, 1.943, 1.943, 1.946, 1.949, + 1.951, 1.946, 1.944, 1.942, 1.943, 1.943, 1.947, 1.948, 1.949, 1.947, 1.945, 1.941, 1.938, 1.939, 1.948, 1.952 + ] + }, + { + "ct": 3850, + "table": + [ + 1.726, 1.724, 1.722, 1.723, 1.731, 1.735, 1.743, 1.746, 1.746, 1.741, 1.735, 1.729, 1.725, 1.721, 1.721, 1.721, + 1.724, 1.723, 1.723, 1.727, 1.735, 1.744, 1.749, 1.756, 1.756, 1.749, 1.744, 1.735, 1.727, 1.719, 1.719, 1.719, + 1.723, 1.723, 1.724, 1.735, 1.746, 1.759, 1.767, 1.775, 1.775, 1.766, 1.758, 1.746, 1.735, 1.723, 1.718, 1.716, + 1.723, 1.725, 1.732, 1.746, 1.759, 1.775, 1.782, 1.792, 1.792, 1.782, 1.772, 1.759, 1.745, 1.729, 1.718, 1.716, + 1.725, 1.729, 1.738, 1.756, 1.775, 1.785, 1.796, 1.803, 1.804, 1.794, 1.783, 1.772, 1.757, 1.736, 1.722, 1.718, + 1.728, 1.731, 1.741, 1.759, 1.781, 1.795, 1.803, 1.806, 1.808, 1.805, 1.791, 1.779, 1.762, 1.739, 1.722, 1.721, + 1.727, 1.731, 1.741, 1.759, 1.781, 1.791, 1.799, 1.804, 1.806, 1.801, 1.791, 1.779, 1.762, 1.739, 1.722, 1.717, + 1.722, 1.724, 1.733, 1.751, 1.768, 1.781, 1.791, 1.796, 1.799, 1.791, 1.781, 1.766, 1.754, 1.731, 1.717, 1.714, + 1.718, 1.718, 1.724, 1.737, 1.752, 1.768, 1.776, 1.782, 1.784, 1.781, 1.766, 1.754, 1.737, 1.724, 1.713, 1.709, + 1.716, 1.715, 1.716, 1.725, 1.737, 1.749, 1.756, 1.763, 1.764, 1.762, 1.749, 1.737, 1.724, 1.717, 1.709, 1.708, + 1.715, 1.714, 1.712, 1.715, 1.722, 1.729, 1.736, 1.741, 1.742, 1.739, 1.731, 1.723, 1.717, 1.712, 1.711, 1.709, + 1.716, 1.714, 1.711, 1.712, 1.715, 1.719, 1.723, 1.728, 1.731, 1.729, 1.723, 1.718, 1.711, 1.711, 1.713, 1.713 + ] + }, + { + "ct": 6000, + "table": + [ + 1.374, 1.372, 1.373, 1.374, 1.375, 1.378, 1.378, 1.381, 1.382, 1.382, 1.378, 1.373, 1.372, 1.369, 1.365, 1.365, + 1.371, 1.371, 1.372, 1.374, 1.378, 1.381, 1.384, 1.386, 1.388, 1.387, 1.384, 1.377, 1.372, 1.368, 1.364, 1.362, + 1.369, 1.371, 1.372, 1.377, 1.383, 1.391, 1.394, 1.396, 1.397, 1.395, 1.391, 1.382, 1.374, 1.369, 1.362, 1.361, + 1.369, 1.371, 1.375, 1.383, 1.391, 1.399, 1.402, 1.404, 1.405, 1.403, 1.398, 1.391, 1.379, 1.371, 1.363, 1.361, + 1.371, 1.373, 1.378, 1.388, 1.399, 1.407, 1.411, 1.413, 1.413, 1.411, 1.405, 1.397, 1.385, 1.374, 1.366, 1.362, + 1.371, 1.374, 1.379, 1.389, 1.405, 1.411, 1.414, 1.414, 1.415, 1.415, 1.411, 1.401, 1.388, 1.376, 1.367, 1.363, + 1.371, 1.373, 1.379, 1.389, 1.405, 1.408, 1.413, 1.414, 1.414, 1.413, 1.409, 1.401, 1.388, 1.376, 1.367, 1.362, + 1.366, 1.369, 1.374, 1.384, 1.396, 1.404, 1.407, 1.408, 1.408, 1.408, 1.401, 1.395, 1.382, 1.371, 1.363, 1.359, + 1.364, 1.365, 1.368, 1.375, 1.386, 1.396, 1.399, 1.401, 1.399, 1.399, 1.395, 1.385, 1.374, 1.365, 1.359, 1.357, + 1.361, 1.363, 1.365, 1.368, 1.377, 1.384, 1.388, 1.391, 1.391, 1.388, 1.385, 1.375, 1.366, 1.361, 1.358, 1.356, + 1.361, 1.362, 1.362, 1.364, 1.367, 1.373, 1.376, 1.377, 1.377, 1.375, 1.373, 1.366, 1.362, 1.358, 1.358, 1.358, + 1.361, 1.362, 1.362, 1.362, 1.363, 1.367, 1.369, 1.368, 1.367, 1.367, 1.367, 1.364, 1.358, 1.357, 1.358, 1.359 + ] + } + ], + "luminance_lut": + [ + 2.716, 2.568, 2.299, 2.065, 1.845, 1.693, 1.605, 1.597, 1.596, 1.634, 1.738, 1.914, 2.145, 2.394, 2.719, 2.901, + 2.593, 2.357, 2.093, 1.876, 1.672, 1.528, 1.438, 1.393, 1.394, 1.459, 1.569, 1.731, 1.948, 2.169, 2.481, 2.756, + 2.439, 2.197, 1.922, 1.691, 1.521, 1.365, 1.266, 1.222, 1.224, 1.286, 1.395, 1.573, 1.747, 1.988, 2.299, 2.563, + 2.363, 2.081, 1.797, 1.563, 1.376, 1.244, 1.152, 1.099, 1.101, 1.158, 1.276, 1.421, 1.607, 1.851, 2.163, 2.455, + 2.342, 2.003, 1.715, 1.477, 1.282, 1.152, 1.074, 1.033, 1.035, 1.083, 1.163, 1.319, 1.516, 1.759, 2.064, 2.398, + 2.342, 1.985, 1.691, 1.446, 1.249, 1.111, 1.034, 1.004, 1.004, 1.028, 1.114, 1.274, 1.472, 1.716, 2.019, 2.389, + 2.342, 1.991, 1.691, 1.446, 1.249, 1.112, 1.034, 1.011, 1.005, 1.035, 1.114, 1.274, 1.472, 1.716, 2.019, 2.389, + 2.365, 2.052, 1.751, 1.499, 1.299, 1.171, 1.089, 1.039, 1.042, 1.084, 1.162, 1.312, 1.516, 1.761, 2.059, 2.393, + 2.434, 2.159, 1.856, 1.601, 1.403, 1.278, 1.166, 1.114, 1.114, 1.162, 1.266, 1.402, 1.608, 1.847, 2.146, 2.435, + 2.554, 2.306, 2.002, 1.748, 1.563, 1.396, 1.299, 1.247, 1.243, 1.279, 1.386, 1.551, 1.746, 1.977, 2.272, 2.518, + 2.756, 2.493, 2.195, 1.947, 1.739, 1.574, 1.481, 1.429, 1.421, 1.457, 1.559, 1.704, 1.929, 2.159, 2.442, 2.681, + 2.935, 2.739, 2.411, 2.151, 1.922, 1.749, 1.663, 1.628, 1.625, 1.635, 1.716, 1.872, 2.113, 2.368, 2.663, 2.824 + ], + "sigma": 0.00381, + "sigma_Cb": 0.00216 + } + }, + { + "rpi.contrast": + { + "ce_enable": 1, + "gamma_curve": + [ + 0, 0, + 1024, 5040, + 2048, 9338, + 3072, 12356, + 4096, 15312, + 5120, 18051, + 6144, 20790, + 7168, 23193, + 8192, 25744, + 9216, 27942, + 10240, 30035, + 11264, 32005, + 12288, 33975, + 13312, 35815, + 14336, 37600, + 15360, 39168, + 16384, 40642, + 18432, 43379, + 20480, 45749, + 22528, 47753, + 24576, 49621, + 26624, 51253, + 28672, 52698, + 30720, 53796, + 32768, 54876, + 36864, 57012, + 40960, 58656, + 45056, 59954, + 49152, 61183, + 53248, 62355, + 57344, 63419, + 61440, 64476, + 65535, 65535 + ] + } + }, + { + "rpi.ccm": + { + "ccms": [ + { + "ct": 2498, + "ccm": + [ + 1.58731, -0.18011, -0.40721, + -0.60639, 2.03422, -0.42782, + -0.19612, -1.69203, 2.88815 + ] + }, + { + "ct": 2811, + "ccm": + [ + 1.61593, -0.33164, -0.28429, + -0.55048, 1.97779, -0.42731, + -0.12042, -1.42847, 2.54889 + ] + }, + { + "ct": 2911, + "ccm": + [ + 1.62771, -0.41282, -0.21489, + -0.57991, 2.04176, -0.46186, + -0.07613, -1.13359, 2.20972 + ] + }, + { + "ct": 2919, + "ccm": + [ + 1.62661, -0.37736, -0.24925, + -0.52519, 1.95233, -0.42714, + -0.10842, -1.34929, 2.45771 + ] + }, + { + "ct": 3627, + "ccm": + [ + 1.70385, -0.57231, -0.13154, + -0.47763, 1.85998, -0.38235, + -0.07467, -0.82678, 1.90145 + ] + }, + { + "ct": 4600, + "ccm": + [ + 1.68486, -0.61085, -0.07402, + -0.41927, 2.04016, -0.62089, + -0.08633, -0.67672, 1.76305 + ] + }, + { + "ct": 5716, + "ccm": + [ + 1.80439, -0.73699, -0.06739, + -0.36073, 1.83327, -0.47255, + -0.08378, -0.56403, 1.64781 + ] + }, + { + "ct": 8575, + "ccm": + [ + 1.89357, -0.76427, -0.12931, + -0.27399, 2.15605, -0.88206, + -0.12035, -0.68256, 1.80292 + ] + } + ] + } + }, + { + "rpi.sharpen": { } + } + ] +} \ No newline at end of file diff --git a/src/ipa/rpi/vc4/data/imx290.json b/src/ipa/rpi/vc4/data/imx290.json new file mode 100644 index 00000000..ace68d0e --- /dev/null +++ b/src/ipa/rpi/vc4/data/imx290.json @@ -0,0 +1,200 @@ +{ + "version": 2.0, + "target": "bcm2835", + "algorithms": [ + { + "rpi.black_level": + { + "black_level": 3840 + } + }, + { + "rpi.dpc": { } + }, + { + "rpi.lux": + { + "reference_shutter_speed": 6813, + "reference_gain": 1.0, + "reference_aperture": 1.0, + "reference_lux": 890, + "reference_Y": 12900 + } + }, + { + "rpi.noise": + { + "reference_constant": 0, + "reference_slope": 2.67 + } + }, + { + "rpi.geq": + { + "offset": 187, + "slope": 0.00842 + } + }, + { + "rpi.sdn": { } + }, + { + "rpi.awb": + { + "bayes": 0 + } + }, + { + "rpi.agc": + { + "speed": 0.2, + "metering_modes": + { + "matrix": + { + "weights": [ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ] + }, + "centre-weighted": + { + "weights": [ 3, 3, 3, 2, 2, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0 ] + }, + "spot": + { + "weights": [ 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ] + } + }, + "exposure_modes": + { + "normal": + { + "shutter": [ 10, 30000, 60000 ], + "gain": [ 1.0, 2.0, 8.0 ] + }, + "sport": + { + "shutter": [ 10, 5000, 10000, 20000, 120000 ], + "gain": [ 1.0, 2.0, 4.0, 6.0, 8.0 ] + } + }, + "constraint_modes": + { + "normal": [ ], + "highlight": [ + { + "bound": "LOWER", + "q_lo": 0.98, + "q_hi": 1.0, + "y_target": + [ + 0, 0.5, + 1000, 0.5 + ] + }, + { + "bound": "UPPER", + "q_lo": 0.98, + "q_hi": 1.0, + "y_target": + [ + 0, 0.8, + 1000, 0.8 + ] + } + ] + }, + "y_target": + [ + 0, 0.16, + 1000, 0.16, + 10000, 0.16 + ] + } + }, + { + "rpi.alsc": + { + "omega": 1.3, + "n_iter": 100, + "luminance_strength": 0.7, + "luminance_lut": + [ + 2.844, 2.349, 2.018, 1.775, 1.599, 1.466, 1.371, 1.321, 1.306, 1.316, 1.357, 1.439, 1.552, 1.705, 1.915, 2.221, + 2.576, 2.151, 1.851, 1.639, 1.478, 1.358, 1.272, 1.231, 1.218, 1.226, 1.262, 1.335, 1.438, 1.571, 1.766, 2.067, + 2.381, 2.005, 1.739, 1.545, 1.389, 1.278, 1.204, 1.166, 1.153, 1.161, 1.194, 1.263, 1.356, 1.489, 1.671, 1.943, + 2.242, 1.899, 1.658, 1.481, 1.329, 1.225, 1.156, 1.113, 1.096, 1.107, 1.143, 1.201, 1.289, 1.423, 1.607, 1.861, + 2.152, 1.831, 1.602, 1.436, 1.291, 1.193, 1.121, 1.069, 1.047, 1.062, 1.107, 1.166, 1.249, 1.384, 1.562, 1.801, + 2.104, 1.795, 1.572, 1.407, 1.269, 1.174, 1.099, 1.041, 1.008, 1.029, 1.083, 1.146, 1.232, 1.364, 1.547, 1.766, + 2.104, 1.796, 1.572, 1.403, 1.264, 1.171, 1.097, 1.036, 1.001, 1.025, 1.077, 1.142, 1.231, 1.363, 1.549, 1.766, + 2.148, 1.827, 1.594, 1.413, 1.276, 1.184, 1.114, 1.062, 1.033, 1.049, 1.092, 1.153, 1.242, 1.383, 1.577, 1.795, + 2.211, 1.881, 1.636, 1.455, 1.309, 1.214, 1.149, 1.104, 1.081, 1.089, 1.125, 1.184, 1.273, 1.423, 1.622, 1.846, + 2.319, 1.958, 1.698, 1.516, 1.362, 1.262, 1.203, 1.156, 1.137, 1.142, 1.171, 1.229, 1.331, 1.484, 1.682, 1.933, + 2.459, 2.072, 1.789, 1.594, 1.441, 1.331, 1.261, 1.219, 1.199, 1.205, 1.232, 1.301, 1.414, 1.571, 1.773, 2.052, + 2.645, 2.206, 1.928, 1.728, 1.559, 1.451, 1.352, 1.301, 1.282, 1.289, 1.319, 1.395, 1.519, 1.685, 1.904, 2.227 + ], + "sigma": 0.005, + "sigma_Cb": 0.005 + } + }, + { + "rpi.contrast": + { + "ce_enable": 1, + "gamma_curve": + [ + 0, 0, + 1024, 5040, + 2048, 9338, + 3072, 12356, + 4096, 15312, + 5120, 18051, + 6144, 20790, + 7168, 23193, + 8192, 25744, + 9216, 27942, + 10240, 30035, + 11264, 32005, + 12288, 33975, + 13312, 35815, + 14336, 37600, + 15360, 39168, + 16384, 40642, + 18432, 43379, + 20480, 45749, + 22528, 47753, + 24576, 49621, + 26624, 51253, + 28672, 52698, + 30720, 53796, + 32768, 54876, + 36864, 57012, + 40960, 58656, + 45056, 59954, + 49152, 61183, + 53248, 62355, + 57344, 63419, + 61440, 64476, + 65535, 65535 + ] + } + }, + { + "rpi.sharpen": { } + }, + { + "rpi.ccm": + { + "ccms": [ + { + "ct": 3900, + "ccm": + [ + 1.54659, -0.17707, -0.36953, + -0.51471, 1.72733, -0.21262, + 0.06667, -0.92279, 1.85612 + ] + } + ] + } + } + ] +} \ No newline at end of file diff --git a/src/ipa/rpi/vc4/data/imx296.json b/src/ipa/rpi/vc4/data/imx296.json new file mode 100644 index 00000000..ae8722c4 --- /dev/null +++ b/src/ipa/rpi/vc4/data/imx296.json @@ -0,0 +1,537 @@ +{ + "version": 2.0, + "target": "bcm2835", + "algorithms": [ + { + "rpi.black_level": + { + "black_level": 3840 + } + }, + { + "rpi.dpc": { } + }, + { + "rpi.lux": + { + "reference_shutter_speed": 7598, + "reference_gain": 1.0, + "reference_aperture": 1.0, + "reference_lux": 800, + "reference_Y": 14028 + } + }, + { + "rpi.noise": + { + "reference_constant": 0, + "reference_slope": 2.671 + } + }, + { + "rpi.geq": + { + "offset": 215, + "slope": 0.01058 + } + }, + { + "rpi.sdn": { } + }, + { + "rpi.awb": + { + "priors": [ + { + "lux": 0, + "prior": + [ + 2000, 1.0, + 3000, 0.0, + 13000, 0.0 + ] + }, + { + "lux": 800, + "prior": + [ + 2000, 0.0, + 6000, 2.0, + 13000, 2.0 + ] + }, + { + "lux": 1500, + "prior": + [ + 2000, 0.0, + 4000, 1.0, + 6000, 6.0, + 6500, 7.0, + 7000, 1.0, + 13000, 1.0 + ] + } + ], + "modes": + { + "auto": + { + "lo": 2500, + "hi": 7600 + }, + "incandescent": + { + "lo": 2500, + "hi": 3000 + }, + "tungsten": + { + "lo": 3000, + "hi": 3500 + }, + "fluorescent": + { + "lo": 4000, + "hi": 4700 + }, + "indoor": + { + "lo": 3000, + "hi": 5000 + }, + "daylight": + { + "lo": 5500, + "hi": 6500 + }, + "cloudy": + { + "lo": 7000, + "hi": 7600 + } + }, + "bayes": 1, + "ct_curve": + [ + 2500.0, 0.5386, 0.2458, + 2800.0, 0.4883, 0.3303, + 2900.0, 0.4855, 0.3349, + 3620.0, 0.4203, 0.4367, + 4560.0, 0.3455, 0.5444, + 5600.0, 0.2948, 0.6124, + 7400.0, 0.2336, 0.6894 + ], + "sensitivity_r": 1.05, + "sensitivity_b": 1.05, + "transverse_pos": 0.03093, + "transverse_neg": 0.02374 + } + }, + { + "rpi.agc": + { + "metering_modes": + { + "centre-weighted": + { + "weights": [ 3, 3, 3, 2, 2, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0 ] + }, + "spot": + { + "weights": [ 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ] + }, + "matrix": + { + "weights": [ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ] + } + }, + "exposure_modes": + { + "normal": + { + "shutter": [ 100, 30000, 45000, 60000, 120000 ], + "gain": [ 1.0, 1.0, 2.0, 4.0, 12.0 ] + }, + "short": + { + "shutter": [ 100, 5000, 10000, 20000, 30000 ], + "gain": [ 1.0, 2.0, 4.0, 8.0, 16.0 ] + } + }, + "constraint_modes": + { + "normal": [ + { + "bound": "LOWER", + "q_lo": 0.98, + "q_hi": 1.0, + "y_target": + [ + 0, 0.5, + 1000, 0.5 + ] + } + ], + "highlight": [ + { + "bound": "LOWER", + "q_lo": 0.98, + "q_hi": 1.0, + "y_target": + [ + 0, 0.5, + 1000, 0.5 + ] + }, + { + "bound": "UPPER", + "q_lo": 0.98, + "q_hi": 1.0, + "y_target": + [ + 0, 0.8, + 1000, 0.8 + ] + } + ] + }, + "y_target": + [ + 0, 0.16, + 1000, 0.165, + 10000, 0.17 + ] + } + }, + { + "rpi.alsc": + { + "omega": 1.3, + "n_iter": 100, + "luminance_strength": 0.5, + "calibrations_Cr": [ + { + "ct": 4000, + "table": + [ + 2.726, 2.736, 2.737, 2.739, 2.741, 2.741, 2.742, 2.742, 2.743, 2.743, 2.742, 2.742, 2.742, 2.742, 2.741, 2.739, + 2.728, 2.736, 2.739, 2.741, 2.742, 2.743, 2.744, 2.745, 2.746, 2.746, 2.745, 2.743, 2.742, 2.742, 2.742, 2.741, + 2.729, 2.737, 2.741, 2.744, 2.746, 2.747, 2.748, 2.749, 2.751, 2.751, 2.749, 2.746, 2.744, 2.743, 2.743, 2.743, + 2.729, 2.738, 2.743, 2.746, 2.749, 2.749, 2.751, 2.752, 2.753, 2.753, 2.752, 2.751, 2.746, 2.744, 2.744, 2.746, + 2.728, 2.737, 2.742, 2.746, 2.749, 2.751, 2.754, 2.755, 2.754, 2.755, 2.754, 2.751, 2.748, 2.746, 2.747, 2.748, + 2.724, 2.738, 2.742, 2.746, 2.749, 2.752, 2.755, 2.755, 2.755, 2.755, 2.754, 2.752, 2.749, 2.749, 2.748, 2.748, + 2.726, 2.738, 2.741, 2.745, 2.749, 2.753, 2.754, 2.755, 2.755, 2.755, 2.754, 2.753, 2.749, 2.748, 2.748, 2.748, + 2.726, 2.738, 2.741, 2.745, 2.746, 2.752, 2.753, 2.753, 2.753, 2.753, 2.754, 2.751, 2.748, 2.748, 2.746, 2.745, + 2.726, 2.736, 2.738, 2.742, 2.745, 2.749, 2.752, 2.753, 2.752, 2.752, 2.751, 2.749, 2.747, 2.745, 2.744, 2.742, + 2.724, 2.733, 2.736, 2.739, 2.742, 2.745, 2.748, 2.749, 2.749, 2.748, 2.748, 2.747, 2.744, 2.743, 2.742, 2.741, + 2.722, 2.726, 2.733, 2.735, 2.737, 2.741, 2.743, 2.744, 2.744, 2.744, 2.744, 2.742, 2.741, 2.741, 2.739, 2.737, + 2.719, 2.722, 2.727, 2.729, 2.731, 2.732, 2.734, 2.734, 2.735, 2.735, 2.735, 2.734, 2.733, 2.732, 2.732, 2.732 + ] + }, + { + "ct": 6000, + "table": + [ + 3.507, 3.522, 3.525, 3.527, 3.531, 3.533, 3.534, 3.535, 3.535, 3.536, 3.536, 3.537, 3.537, 3.538, 3.537, 3.536, + 3.511, 3.524, 3.528, 3.532, 3.533, 3.535, 3.537, 3.538, 3.538, 3.541, 3.539, 3.539, 3.539, 3.539, 3.538, 3.538, + 3.513, 3.528, 3.532, 3.535, 3.538, 3.542, 3.543, 3.546, 3.548, 3.551, 3.547, 3.543, 3.541, 3.541, 3.541, 3.541, + 3.513, 3.528, 3.533, 3.539, 3.544, 3.546, 3.548, 3.552, 3.553, 3.553, 3.552, 3.548, 3.543, 3.542, 3.542, 3.545, + 3.513, 3.528, 3.534, 3.541, 3.547, 3.549, 3.552, 3.553, 3.554, 3.554, 3.553, 3.549, 3.546, 3.544, 3.547, 3.549, + 3.508, 3.528, 3.533, 3.541, 3.548, 3.551, 3.553, 3.554, 3.555, 3.555, 3.555, 3.551, 3.548, 3.547, 3.549, 3.551, + 3.511, 3.529, 3.534, 3.541, 3.548, 3.551, 3.553, 3.555, 3.555, 3.555, 3.556, 3.554, 3.549, 3.548, 3.548, 3.548, + 3.511, 3.528, 3.533, 3.539, 3.546, 3.549, 3.553, 3.554, 3.554, 3.554, 3.554, 3.553, 3.549, 3.547, 3.547, 3.547, + 3.511, 3.527, 3.533, 3.536, 3.541, 3.547, 3.551, 3.553, 3.553, 3.552, 3.551, 3.551, 3.548, 3.544, 3.542, 3.543, + 3.507, 3.523, 3.528, 3.533, 3.538, 3.541, 3.546, 3.548, 3.549, 3.548, 3.548, 3.546, 3.542, 3.541, 3.541, 3.541, + 3.505, 3.514, 3.523, 3.527, 3.532, 3.537, 3.538, 3.544, 3.544, 3.544, 3.542, 3.541, 3.537, 3.537, 3.536, 3.535, + 3.503, 3.508, 3.515, 3.519, 3.521, 3.523, 3.524, 3.525, 3.526, 3.526, 3.527, 3.526, 3.524, 3.526, 3.527, 3.527 + ] + } + ], + "calibrations_Cb": [ + { + "ct": 4000, + "table": + [ + 2.032, 2.037, 2.039, 2.041, 2.041, 2.042, 2.043, 2.044, 2.045, 2.045, 2.044, 2.043, 2.042, 2.041, 2.041, 2.034, + 2.032, 2.036, 2.039, 2.041, 2.042, 2.042, 2.043, 2.044, 2.045, 2.046, 2.045, 2.044, 2.042, 2.041, 2.039, 2.035, + 2.032, 2.036, 2.038, 2.041, 2.043, 2.044, 2.044, 2.045, 2.046, 2.047, 2.047, 2.045, 2.043, 2.042, 2.041, 2.037, + 2.032, 2.035, 2.039, 2.042, 2.043, 2.044, 2.045, 2.046, 2.048, 2.048, 2.047, 2.046, 2.045, 2.044, 2.042, 2.039, + 2.031, 2.034, 2.037, 2.039, 2.043, 2.045, 2.045, 2.046, 2.047, 2.047, 2.047, 2.046, 2.045, 2.044, 2.043, 2.039, + 2.029, 2.033, 2.036, 2.039, 2.042, 2.043, 2.045, 2.046, 2.046, 2.046, 2.046, 2.046, 2.046, 2.045, 2.044, 2.041, + 2.028, 2.032, 2.035, 2.039, 2.041, 2.043, 2.044, 2.045, 2.045, 2.046, 2.046, 2.046, 2.046, 2.045, 2.044, 2.039, + 2.027, 2.032, 2.035, 2.038, 2.039, 2.041, 2.044, 2.044, 2.044, 2.045, 2.046, 2.046, 2.046, 2.045, 2.044, 2.039, + 2.027, 2.031, 2.034, 2.035, 2.037, 2.039, 2.042, 2.043, 2.044, 2.045, 2.045, 2.046, 2.045, 2.044, 2.043, 2.038, + 2.025, 2.028, 2.032, 2.034, 2.036, 2.037, 2.041, 2.042, 2.043, 2.044, 2.044, 2.044, 2.044, 2.043, 2.041, 2.036, + 2.024, 2.026, 2.029, 2.032, 2.034, 2.036, 2.038, 2.041, 2.041, 2.042, 2.043, 2.042, 2.041, 2.041, 2.037, 2.036, + 2.022, 2.024, 2.027, 2.029, 2.032, 2.034, 2.036, 2.039, 2.039, 2.039, 2.041, 2.039, 2.039, 2.038, 2.036, 2.034 + ] + }, + { + "ct": 6000, + "table": + [ + 1.585, 1.587, 1.589, 1.589, 1.589, 1.591, 1.591, 1.591, 1.591, 1.591, 1.589, 1.589, 1.588, 1.588, 1.587, 1.581, + 1.585, 1.587, 1.588, 1.589, 1.591, 1.591, 1.591, 1.591, 1.591, 1.591, 1.591, 1.589, 1.588, 1.588, 1.587, 1.582, + 1.585, 1.586, 1.588, 1.589, 1.591, 1.591, 1.591, 1.591, 1.592, 1.592, 1.591, 1.591, 1.589, 1.588, 1.587, 1.584, + 1.585, 1.586, 1.588, 1.589, 1.591, 1.592, 1.592, 1.592, 1.593, 1.593, 1.592, 1.591, 1.589, 1.589, 1.588, 1.586, + 1.584, 1.586, 1.587, 1.589, 1.591, 1.591, 1.592, 1.592, 1.592, 1.592, 1.591, 1.591, 1.591, 1.589, 1.589, 1.586, + 1.583, 1.585, 1.587, 1.588, 1.589, 1.591, 1.591, 1.592, 1.592, 1.591, 1.591, 1.591, 1.591, 1.591, 1.589, 1.586, + 1.583, 1.584, 1.586, 1.588, 1.589, 1.589, 1.591, 1.591, 1.591, 1.591, 1.591, 1.591, 1.591, 1.591, 1.589, 1.585, + 1.581, 1.584, 1.586, 1.587, 1.588, 1.588, 1.589, 1.591, 1.591, 1.591, 1.591, 1.591, 1.591, 1.589, 1.589, 1.585, + 1.581, 1.583, 1.584, 1.586, 1.587, 1.588, 1.589, 1.589, 1.591, 1.591, 1.591, 1.591, 1.591, 1.589, 1.589, 1.585, + 1.579, 1.581, 1.583, 1.584, 1.586, 1.586, 1.588, 1.589, 1.589, 1.589, 1.589, 1.589, 1.589, 1.589, 1.587, 1.584, + 1.578, 1.579, 1.581, 1.583, 1.584, 1.585, 1.586, 1.587, 1.588, 1.588, 1.588, 1.588, 1.588, 1.587, 1.585, 1.583, + 1.577, 1.578, 1.579, 1.582, 1.583, 1.584, 1.585, 1.586, 1.586, 1.587, 1.587, 1.587, 1.586, 1.586, 1.584, 1.583 + ] + } + ], + "luminance_lut": + [ + 1.112, 1.098, 1.078, 1.062, 1.049, 1.039, 1.031, 1.027, 1.026, 1.027, 1.034, 1.043, 1.054, 1.069, 1.087, 1.096, + 1.106, 1.091, 1.073, 1.056, 1.042, 1.032, 1.025, 1.021, 1.021, 1.022, 1.027, 1.036, 1.047, 1.061, 1.077, 1.088, + 1.101, 1.085, 1.066, 1.049, 1.035, 1.026, 1.019, 1.013, 1.013, 1.015, 1.021, 1.028, 1.039, 1.052, 1.069, 1.083, + 1.098, 1.081, 1.059, 1.045, 1.031, 1.021, 1.013, 1.007, 1.007, 1.009, 1.014, 1.021, 1.033, 1.046, 1.063, 1.081, + 1.097, 1.076, 1.057, 1.041, 1.027, 1.016, 1.007, 1.004, 1.002, 1.005, 1.009, 1.017, 1.028, 1.043, 1.061, 1.077, + 1.096, 1.075, 1.054, 1.039, 1.025, 1.014, 1.005, 1.001, 1.001, 1.002, 1.006, 1.015, 1.027, 1.041, 1.058, 1.076, + 1.096, 1.074, 1.054, 1.039, 1.025, 1.013, 1.005, 1.001, 1.001, 1.001, 1.006, 1.015, 1.026, 1.041, 1.058, 1.076, + 1.096, 1.075, 1.056, 1.041, 1.026, 1.014, 1.007, 1.003, 1.002, 1.004, 1.008, 1.016, 1.028, 1.041, 1.059, 1.076, + 1.096, 1.079, 1.059, 1.044, 1.029, 1.018, 1.011, 1.007, 1.005, 1.008, 1.012, 1.019, 1.031, 1.044, 1.061, 1.077, + 1.101, 1.084, 1.065, 1.049, 1.035, 1.024, 1.017, 1.011, 1.011, 1.012, 1.018, 1.025, 1.036, 1.051, 1.068, 1.081, + 1.106, 1.092, 1.072, 1.055, 1.042, 1.033, 1.024, 1.019, 1.018, 1.019, 1.025, 1.032, 1.044, 1.058, 1.076, 1.088, + 1.113, 1.097, 1.079, 1.063, 1.049, 1.039, 1.031, 1.025, 1.025, 1.025, 1.031, 1.039, 1.051, 1.065, 1.083, 1.094 + ], + "sigma": 0.00047, + "sigma_Cb": 0.00056 + } + }, + { + "rpi.contrast": + { + "ce_enable": 1, + "gamma_curve": + [ + 0, 0, + 1024, 5040, + 2048, 9338, + 3072, 12356, + 4096, 15312, + 5120, 18051, + 6144, 20790, + 7168, 23193, + 8192, 25744, + 9216, 27942, + 10240, 30035, + 11264, 32005, + 12288, 33975, + 13312, 35815, + 14336, 37600, + 15360, 39168, + 16384, 40642, + 18432, 43379, + 20480, 45749, + 22528, 47753, + 24576, 49621, + 26624, 51253, + 28672, 52698, + 30720, 53796, + 32768, 54876, + 36864, 57012, + 40960, 58656, + 45056, 59954, + 49152, 61183, + 53248, 62355, + 57344, 63419, + 61440, 64476, + 65535, 65535 + ] + } + }, + { + "rpi.ccm": + { + "ccms": [ + { + "ct": 2000, + "ccm": + [ + 1.48716, -0.1877, -0.35079, + -0.48577, 1.55088, -0.03387, + 0.24919, -1.4583, 2.12083 + ] + }, + { + "ct": 2200, + "ccm": + [ + 1.53439, -0.28852, -0.29392, + -0.44748, 1.56295, -0.08907, + 0.23529, -1.30488, 1.99784 + ] + }, + { + "ct": 2400, + "ccm": + [ + 1.57619, -0.36904, -0.25181, + -0.41654, 1.57046, -0.13192, + 0.21678, -1.18352, 1.90786 + ] + }, + { + "ct": 2600, + "ccm": + [ + 1.61348, -0.43497, -0.2198, + -0.39075, 1.5753, -0.1665, + 0.19789, -1.08592, 1.83942 + ] + }, + { + "ct": 2800, + "ccm": + [ + 1.64717, -0.49009, -0.1951, + -0.36881, 1.57852, -0.1952, + 0.18016, -1.00609, 1.78575 + ] + }, + { + "ct": 3000, + "ccm": + [ + 1.67798, -0.53693, -0.17591, + -0.34986, 1.58074, -0.21955, + 0.16406, -0.9398, 1.74261 + ] + }, + { + "ct": 3200, + "ccm": + [ + 1.70647, -0.5773, -0.161, + -0.33332, 1.58235, -0.24056, + 0.14961, -0.88398, 1.70721 + ] + }, + { + "ct": 3400, + "ccm": + [ + 1.73305, -0.61248, -0.14951, + -0.31875, 1.58355, -0.25894, + 0.13671, -0.83642, 1.67769 + ] + }, + { + "ct": 3600, + "ccm": + [ + 1.75802, -0.64343, -0.14077, + -0.30581, 1.5845, -0.27518, + 0.12518, -0.79546, 1.65271 + ] + }, + { + "ct": 4100, + "ccm": + [ + 1.78116, -0.67459, -0.13048, + -0.26859, 1.58692, -0.31929, + 0.11915, -0.77931, 1.64012 + ] + }, + { + "ct": 4600, + "ccm": + [ + 1.83867, -0.73605, -0.12044, + -0.24947, 1.58699, -0.34207, + 0.09949, -0.71041, 1.59842 + ] + }, + { + "ct": 5100, + "ccm": + [ + 1.88967, -0.78455, -0.11744, + -0.23398, 1.58806, -0.36172, + 0.08362, -0.6574, 1.56728 + ] + }, + { + "ct": 5600, + "ccm": + [ + 1.93485, -0.82318, -0.1191, + -0.22108, 1.58973, -0.37892, + 0.07074, -0.61609, 1.54362 + ] + }, + { + "ct": 6100, + "ccm": + [ + 1.97481, -0.85423, -0.12371, + -0.21015, 1.59169, -0.39406, + 0.06021, -0.58353, 1.52536 + ] + }, + { + "ct": 6600, + "ccm": + [ + 2.01029, -0.87946, -0.13017, + -0.20074, 1.59378, -0.4075, + 0.05146, -0.55732, 1.51096 + ] + }, + { + "ct": 7100, + "ccm": + [ + 2.04183, -0.9002, -0.13765, + -0.19255, 1.59586, -0.41944, + 0.04414, -0.53603, 1.49947 + ] + }, + { + "ct": 7600, + "ccm": + [ + 2.07001, -0.91744, -0.14566, + -0.18534, 1.59788, -0.43013, + 0.03791, -0.51841, 1.49013 + ] + }, + { + "ct": 8100, + "ccm": + [ + 2.09534, -0.93195, -0.15388, + -0.17893, 1.59981, -0.43974, + 0.03256, -0.50364, 1.48243 + ] + }, + { + "ct": 8600, + "ccm": + [ + 2.11799, -0.94416, -0.16203, + -0.17324, 1.60161, -0.44836, + 0.02795, -0.4912, 1.47604 + ] + } + ] + } + }, + { + "rpi.sharpen": + { + "threshold": 0.1, + "strength": 1.0, + "limit": 0.18 + } + } + ] +} diff --git a/src/ipa/rpi/vc4/data/imx296_mono.json b/src/ipa/rpi/vc4/data/imx296_mono.json new file mode 100644 index 00000000..30965b4b --- /dev/null +++ b/src/ipa/rpi/vc4/data/imx296_mono.json @@ -0,0 +1,233 @@ +{ + "version": 2.0, + "target": "bcm2835", + "algorithms": [ + { + "rpi.black_level": + { + "black_level": 3840 + } + }, + { + "rpi.dpc": { } + }, + { + "rpi.lux": + { + "reference_shutter_speed": 19184, + "reference_gain": 1.0, + "reference_aperture": 1.0, + "reference_lux": 432, + "reference_Y": 13773 + } + }, + { + "rpi.noise": + { + "reference_constant": 0, + "reference_slope": 2.957 + } + }, + { + "rpi.geq": + { + "offset": 185, + "slope": 0.0105 + } + }, + { + "rpi.sdn": { } + }, + { + "rpi.agc": + { + "metering_modes": + { + "centre-weighted": + { + "weights": [ 3, 3, 3, 2, 2, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0 ] + }, + "spot": + { + "weights": [ 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ] + }, + "matrix": + { + "weights": [ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ] + } + }, + "exposure_modes": + { + "normal": + { + "shutter": [ 100, 10000, 30000, 60000, 120000 ], + "gain": [ 1.0, 2.0, 4.0, 6.0, 6.0 ] + }, + "short": + { + "shutter": [ 100, 5000, 10000, 20000, 120000 ], + "gain": [ 1.0, 2.0, 4.0, 6.0, 6.0 ] + } + }, + "constraint_modes": + { + "normal": [ + { + "bound": "LOWER", + "q_lo": 0.98, + "q_hi": 1.0, + "y_target": + [ + 0, 0.5, + 1000, 0.5 + ] + } + ], + "highlight": [ + { + "bound": "LOWER", + "q_lo": 0.98, + "q_hi": 1.0, + "y_target": + [ + 0, 0.5, + 1000, 0.5 + ] + }, + { + "bound": "UPPER", + "q_lo": 0.98, + "q_hi": 1.0, + "y_target": + [ + 0, 0.8, + 1000, 0.8 + ] + } + ] + }, + "y_target": + [ + 0, 0.16, + 1000, 0.165, + 10000, 0.17 + ] + } + }, + { + "rpi.alsc": + { + "omega": 1.3, + "n_iter": 0, + "luminance_strength": 0.5, + "calibrations_Cr": [ + { + "ct": 4000, + "table": + [ + 2.554, 2.554, 2.541, 2.534, 2.495, 2.506, 2.516, 2.517, 2.518, 2.515, 2.513, 2.495, 2.481, 2.533, 2.533, 2.521, + 2.522, 2.534, 2.539, 2.531, 2.531, 2.506, 2.506, 2.513, 2.513, 2.509, 2.498, 2.496, 2.508, 2.517, 2.521, 2.521, + 2.509, 2.517, 2.534, 2.529, 2.531, 2.521, 2.517, 2.517, 2.515, 2.514, 2.506, 2.499, 2.508, 2.508, 2.521, 2.537, + 2.507, 2.508, 2.517, 2.516, 2.495, 2.487, 2.519, 2.534, 2.535, 2.531, 2.499, 2.494, 2.501, 2.511, 2.526, 2.526, + 2.509, 2.517, 2.507, 2.501, 2.494, 2.519, 2.539, 2.539, 2.537, 2.537, 2.533, 2.499, 2.503, 2.511, 2.529, 2.525, + 2.521, 2.522, 2.476, 2.501, 2.501, 2.539, 2.546, 2.538, 2.531, 2.538, 2.541, 2.531, 2.529, 2.526, 2.529, 2.525, + 2.516, 2.519, 2.469, 2.499, 2.499, 2.543, 2.543, 2.531, 2.528, 2.534, 2.541, 2.535, 2.531, 2.526, 2.531, 2.528, + 2.509, 2.515, 2.465, 2.487, 2.487, 2.539, 2.543, 2.539, 2.533, 2.549, 2.542, 2.531, 2.529, 2.524, 2.532, 2.533, + 2.499, 2.499, 2.475, 2.482, 2.471, 2.509, 2.539, 2.544, 2.543, 2.545, 2.533, 2.498, 2.521, 2.521, 2.537, 2.536, + 2.499, 2.488, 2.488, 2.488, 2.471, 2.462, 2.509, 2.539, 2.539, 2.532, 2.498, 2.498, 2.518, 2.518, 2.539, 2.539, + 2.483, 2.484, 2.488, 2.488, 2.502, 2.496, 2.508, 2.514, 2.518, 2.517, 2.521, 2.518, 2.518, 2.518, 2.525, 2.539, + 2.483, 2.487, 2.478, 2.478, 2.507, 2.509, 2.514, 2.513, 2.514, 2.517, 2.536, 2.559, 2.501, 2.501, 2.503, 2.525 + ] + } + ], + "calibrations_Cb": [ + { + "ct": 4000, + "table": + [ + 2.619, 2.603, 2.599, 2.597, 2.595, 2.594, 2.589, 2.587, 2.586, 2.589, 2.592, 2.597, 2.601, 2.608, 2.621, 2.621, + 2.619, 2.615, 2.603, 2.601, 2.596, 2.595, 2.591, 2.589, 2.589, 2.592, 2.599, 2.593, 2.601, 2.613, 2.622, 2.631, + 2.617, 2.617, 2.612, 2.611, 2.604, 2.598, 2.593, 2.591, 2.592, 2.591, 2.593, 2.595, 2.599, 2.614, 2.623, 2.631, + 2.624, 2.619, 2.615, 2.612, 2.605, 2.602, 2.597, 2.596, 2.592, 2.592, 2.595, 2.599, 2.602, 2.606, 2.619, 2.624, + 2.629, 2.627, 2.627, 2.617, 2.609, 2.598, 2.612, 2.623, 2.615, 2.604, 2.589, 2.595, 2.599, 2.608, 2.611, 2.614, + 2.629, 2.632, 2.637, 2.627, 2.612, 2.612, 2.629, 2.631, 2.628, 2.621, 2.604, 2.597, 2.598, 2.604, 2.609, 2.609, + 2.635, 2.636, 2.642, 2.628, 2.623, 2.623, 2.636, 2.636, 2.634, 2.628, 2.616, 2.599, 2.597, 2.601, 2.603, 2.601, + 2.641, 2.639, 2.646, 2.632, 2.627, 2.625, 2.632, 2.635, 2.634, 2.627, 2.614, 2.596, 2.595, 2.599, 2.599, 2.598, + 2.643, 2.644, 2.651, 2.649, 2.629, 2.617, 2.624, 2.629, 2.625, 2.614, 2.586, 2.599, 2.595, 2.597, 2.592, 2.595, + 2.645, 2.646, 2.649, 2.649, 2.638, 2.624, 2.616, 2.617, 2.609, 2.604, 2.603, 2.603, 2.595, 2.589, 2.587, 2.592, + 2.641, 2.643, 2.649, 2.647, 2.638, 2.618, 2.615, 2.608, 2.602, 2.595, 2.596, 2.595, 2.593, 2.584, 2.581, 2.583, + 2.638, 2.637, 2.647, 2.634, 2.634, 2.618, 2.621, 2.621, 2.611, 2.602, 2.596, 2.583, 2.581, 2.581, 2.576, 2.574 + ] + } + ], + "luminance_lut": + [ + 1.308, 1.293, 1.228, 1.175, 1.139, 1.108, 1.092, 1.082, 1.082, 1.086, 1.097, 1.114, 1.149, 1.199, 1.279, 1.303, + 1.293, 1.249, 1.199, 1.162, 1.136, 1.109, 1.087, 1.077, 1.072, 1.081, 1.095, 1.103, 1.133, 1.172, 1.225, 1.282, + 1.251, 1.212, 1.186, 1.159, 1.129, 1.114, 1.102, 1.088, 1.088, 1.088, 1.095, 1.117, 1.123, 1.158, 1.198, 1.249, + 1.223, 1.192, 1.177, 1.163, 1.147, 1.139, 1.132, 1.112, 1.111, 1.107, 1.113, 1.118, 1.139, 1.155, 1.186, 1.232, + 1.207, 1.186, 1.171, 1.162, 1.168, 1.163, 1.153, 1.138, 1.129, 1.128, 1.132, 1.136, 1.149, 1.167, 1.189, 1.216, + 1.198, 1.186, 1.176, 1.176, 1.177, 1.185, 1.171, 1.157, 1.146, 1.144, 1.146, 1.149, 1.161, 1.181, 1.201, 1.221, + 1.203, 1.181, 1.176, 1.178, 1.191, 1.189, 1.188, 1.174, 1.159, 1.153, 1.158, 1.161, 1.169, 1.185, 1.211, 1.227, + 1.211, 1.179, 1.177, 1.187, 1.194, 1.196, 1.194, 1.187, 1.176, 1.169, 1.171, 1.171, 1.175, 1.189, 1.214, 1.226, + 1.219, 1.182, 1.184, 1.191, 1.195, 1.199, 1.197, 1.194, 1.188, 1.185, 1.179, 1.179, 1.182, 1.194, 1.212, 1.227, + 1.237, 1.192, 1.194, 1.194, 1.198, 1.199, 1.198, 1.197, 1.196, 1.193, 1.189, 1.189, 1.192, 1.203, 1.214, 1.231, + 1.282, 1.199, 1.199, 1.197, 1.199, 1.199, 1.192, 1.193, 1.193, 1.194, 1.196, 1.197, 1.206, 1.216, 1.228, 1.244, + 1.309, 1.236, 1.204, 1.203, 1.202, 1.194, 1.194, 1.188, 1.192, 1.192, 1.199, 1.201, 1.212, 1.221, 1.235, 1.247 + ], + "sigma": 0.005, + "sigma_Cb": 0.005 + } + }, + { + "rpi.contrast": + { + "ce_enable": 1, + "gamma_curve": + [ + 0, 0, + 1024, 5040, + 2048, 9338, + 3072, 12356, + 4096, 15312, + 5120, 18051, + 6144, 20790, + 7168, 23193, + 8192, 25744, + 9216, 27942, + 10240, 30035, + 11264, 32005, + 12288, 33975, + 13312, 35815, + 14336, 37600, + 15360, 39168, + 16384, 40642, + 18432, 43379, + 20480, 45749, + 22528, 47753, + 24576, 49621, + 26624, 51253, + 28672, 52698, + 30720, 53796, + 32768, 54876, + 36864, 57012, + 40960, 58656, + 45056, 59954, + 49152, 61183, + 53248, 62355, + 57344, 63419, + 61440, 64476, + 65535, 65535 + ] + } + }, + { + "rpi.sharpen": + { + "threshold": 0.1, + "strength": 1.0, + "limit": 0.18 + } + } + ] +} diff --git a/src/ipa/rpi/vc4/data/imx378.json b/src/ipa/rpi/vc4/data/imx378.json new file mode 100644 index 00000000..8b4ed225 --- /dev/null +++ b/src/ipa/rpi/vc4/data/imx378.json @@ -0,0 +1,413 @@ +{ + "version": 2.0, + "target": "bcm2835", + "algorithms": [ + { + "rpi.black_level": + { + "black_level": 4096 + } + }, + { + "rpi.dpc": { } + }, + { + "rpi.lux": + { + "reference_shutter_speed": 9999, + "reference_gain": 1.95, + "reference_aperture": 1.0, + "reference_lux": 1000, + "reference_Y": 12996 + } + }, + { + "rpi.noise": + { + "reference_constant": 0, + "reference_slope": 2.641 + } + }, + { + "rpi.geq": + { + "offset": 235, + "slope": 0.00902 + } + }, + { + "rpi.sdn": { } + }, + { + "rpi.awb": + { + "priors": [ + { + "lux": 0, + "prior": + [ + 2000, 1.0, + 3000, 0.0, + 13000, 0.0 + ] + }, + { + "lux": 800, + "prior": + [ + 2000, 0.0, + 6000, 2.0, + 13000, 2.0 + ] + }, + { + "lux": 1500, + "prior": + [ + 2000, 0.0, + 4000, 1.0, + 6000, 6.0, + 6500, 7.0, + 7000, 1.0, + 13000, 1.0 + ] + } + ], + "modes": + { + "auto": + { + "lo": 2500, + "hi": 8000 + }, + "incandescent": + { + "lo": 2500, + "hi": 3000 + }, + "tungsten": + { + "lo": 3000, + "hi": 3500 + }, + "fluorescent": + { + "lo": 4000, + "hi": 4700 + }, + "indoor": + { + "lo": 3000, + "hi": 5000 + }, + "daylight": + { + "lo": 5500, + "hi": 6500 + }, + "cloudy": + { + "lo": 7000, + "hi": 8100 + } + }, + "bayes": 1, + "ct_curve": + [ + 2850.0, 0.6361, 0.3911, + 3550.0, 0.5386, 0.5077, + 4500.0, 0.4472, 0.6171, + 5600.0, 0.3906, 0.6848, + 8000.0, 0.3412, 0.7441 + ], + "sensitivity_r": 1.0, + "sensitivity_b": 1.0, + "transverse_pos": 0.01667, + "transverse_neg": 0.01195 + } + }, + { + "rpi.agc": + { + "metering_modes": + { + "centre-weighted": + { + "weights": [ 3, 3, 3, 2, 2, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0 ] + }, + "spot": + { + "weights": [ 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ] + }, + "matrix": + { + "weights": [ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ] + } + }, + "exposure_modes": + { + "normal": + { + "shutter": [ 100, 10000, 30000, 60000, 120000 ], + "gain": [ 1.0, 2.0, 4.0, 6.0, 8.0 ] + }, + "short": + { + "shutter": [ 100, 5000, 10000, 20000, 120000 ], + "gain": [ 1.0, 2.0, 4.0, 6.0, 8.0 ] + } + }, + "constraint_modes": + { + "normal": [ + { + "bound": "LOWER", + "q_lo": 0.98, + "q_hi": 1.0, + "y_target": + [ + 0, 0.5, + 1000, 0.5 + ] + } + ], + "highlight": [ + { + "bound": "LOWER", + "q_lo": 0.98, + "q_hi": 1.0, + "y_target": + [ + 0, 0.5, + 1000, 0.5 + ] + }, + { + "bound": "UPPER", + "q_lo": 0.98, + "q_hi": 1.0, + "y_target": + [ + 0, 0.8, + 1000, 0.8 + ] + } + ] + }, + "y_target": + [ + 0, 0.16, + 1000, 0.165, + 10000, 0.17 + ] + } + }, + { + "rpi.alsc": + { + "omega": 1.3, + "n_iter": 100, + "luminance_strength": 0.5, + "calibrations_Cr": [ + { + "ct": 2800, + "table": + [ + 1.604, 1.601, 1.593, 1.581, 1.568, 1.561, 1.561, 1.561, 1.561, 1.567, 1.582, 1.596, 1.609, 1.622, 1.632, 1.636, + 1.601, 1.594, 1.586, 1.571, 1.555, 1.546, 1.543, 1.543, 1.547, 1.555, 1.572, 1.584, 1.599, 1.614, 1.625, 1.632, + 1.599, 1.586, 1.571, 1.555, 1.542, 1.528, 1.518, 1.518, 1.523, 1.537, 1.555, 1.572, 1.589, 1.607, 1.622, 1.629, + 1.597, 1.579, 1.561, 1.542, 1.528, 1.512, 1.493, 1.493, 1.499, 1.523, 1.537, 1.563, 1.582, 1.601, 1.619, 1.629, + 1.597, 1.577, 1.557, 1.535, 1.512, 1.493, 1.481, 1.479, 1.492, 1.499, 1.524, 1.555, 1.578, 1.599, 1.619, 1.629, + 1.597, 1.577, 1.557, 1.534, 1.508, 1.483, 1.476, 1.476, 1.481, 1.496, 1.522, 1.554, 1.578, 1.599, 1.619, 1.629, + 1.597, 1.578, 1.557, 1.534, 1.508, 1.483, 1.481, 1.479, 1.481, 1.496, 1.522, 1.554, 1.579, 1.601, 1.619, 1.631, + 1.597, 1.581, 1.562, 1.539, 1.517, 1.504, 1.483, 1.481, 1.496, 1.511, 1.531, 1.561, 1.585, 1.607, 1.623, 1.632, + 1.601, 1.589, 1.569, 1.554, 1.539, 1.517, 1.504, 1.504, 1.511, 1.531, 1.553, 1.573, 1.596, 1.614, 1.629, 1.636, + 1.609, 1.601, 1.586, 1.569, 1.554, 1.542, 1.535, 1.535, 1.541, 1.553, 1.573, 1.592, 1.608, 1.625, 1.637, 1.645, + 1.617, 1.611, 1.601, 1.586, 1.574, 1.565, 1.564, 1.564, 1.571, 1.579, 1.592, 1.608, 1.622, 1.637, 1.646, 1.654, + 1.619, 1.617, 1.611, 1.601, 1.588, 1.585, 1.585, 1.585, 1.588, 1.592, 1.607, 1.622, 1.637, 1.645, 1.654, 1.655 + ] + }, + { + "ct": 5500, + "table": + [ + 2.664, 2.658, 2.645, 2.629, 2.602, 2.602, 2.602, 2.606, 2.617, 2.628, 2.649, 2.677, 2.699, 2.722, 2.736, 2.747, + 2.658, 2.653, 2.629, 2.605, 2.576, 2.575, 2.577, 2.592, 2.606, 2.618, 2.629, 2.651, 2.678, 2.707, 2.727, 2.741, + 2.649, 2.631, 2.605, 2.576, 2.563, 2.552, 2.552, 2.557, 2.577, 2.604, 2.619, 2.641, 2.669, 2.698, 2.721, 2.741, + 2.643, 2.613, 2.583, 2.563, 2.552, 2.531, 2.527, 2.527, 2.551, 2.577, 2.604, 2.638, 2.665, 2.694, 2.721, 2.741, + 2.643, 2.606, 2.575, 2.558, 2.531, 2.516, 2.504, 2.516, 2.527, 2.551, 2.596, 2.635, 2.665, 2.694, 2.721, 2.741, + 2.643, 2.606, 2.575, 2.558, 2.531, 2.503, 2.501, 2.502, 2.522, 2.551, 2.592, 2.635, 2.669, 2.696, 2.727, 2.744, + 2.648, 2.611, 2.579, 2.558, 2.532, 2.511, 2.502, 2.511, 2.522, 2.552, 2.592, 2.642, 2.673, 2.702, 2.731, 2.752, + 2.648, 2.619, 2.589, 2.571, 2.556, 2.532, 2.519, 2.522, 2.552, 2.568, 2.605, 2.648, 2.683, 2.715, 2.743, 2.758, + 2.659, 2.637, 2.613, 2.589, 2.571, 2.556, 2.555, 2.555, 2.568, 2.605, 2.641, 2.671, 2.699, 2.729, 2.758, 2.776, + 2.679, 2.665, 2.637, 2.613, 2.602, 2.599, 2.599, 2.606, 2.619, 2.641, 2.671, 2.698, 2.723, 2.754, 2.776, 2.787, + 2.695, 2.684, 2.671, 2.646, 2.636, 2.636, 2.641, 2.648, 2.661, 2.681, 2.698, 2.723, 2.751, 2.776, 2.788, 2.803, + 2.702, 2.699, 2.684, 2.671, 2.664, 2.664, 2.664, 2.668, 2.681, 2.698, 2.723, 2.751, 2.773, 2.788, 2.803, 2.805 + ] + } + ], + "calibrations_Cb": [ + { + "ct": 2800, + "table": + [ + 2.876, 2.868, 2.863, 2.851, 2.846, 2.846, 2.847, 2.851, 2.851, 2.857, 2.867, 2.875, 2.889, 2.899, 2.913, 2.926, + 2.863, 2.861, 2.856, 2.846, 2.846, 2.847, 2.848, 2.851, 2.857, 2.859, 2.875, 2.882, 2.886, 2.896, 2.909, 2.917, + 2.861, 2.856, 2.846, 2.841, 2.841, 2.855, 2.867, 2.875, 2.888, 2.888, 2.885, 2.883, 2.886, 2.889, 2.901, 2.913, + 2.858, 2.851, 2.846, 2.846, 2.855, 2.867, 2.884, 2.895, 2.902, 2.902, 2.901, 2.891, 2.891, 2.894, 2.901, 2.909, + 2.858, 2.851, 2.846, 2.846, 2.867, 2.884, 2.895, 2.902, 2.909, 2.915, 2.911, 2.901, 2.895, 2.898, 2.904, 2.909, + 2.858, 2.851, 2.849, 2.853, 2.874, 2.888, 2.901, 2.909, 2.917, 2.922, 2.917, 2.911, 2.901, 2.899, 2.905, 2.908, + 2.861, 2.855, 2.853, 2.855, 2.874, 2.888, 2.901, 2.913, 2.918, 2.922, 2.921, 2.911, 2.901, 2.901, 2.907, 2.908, + 2.862, 2.859, 2.855, 2.856, 2.872, 2.885, 2.899, 2.906, 2.915, 2.917, 2.911, 2.907, 2.907, 2.907, 2.908, 2.909, + 2.863, 2.863, 2.859, 2.864, 2.871, 2.881, 2.885, 2.899, 2.905, 2.905, 2.904, 2.904, 2.907, 2.909, 2.913, 2.913, + 2.866, 2.865, 2.865, 2.867, 2.868, 2.872, 2.881, 2.885, 2.889, 2.894, 2.895, 2.902, 2.906, 2.913, 2.914, 2.917, + 2.875, 2.875, 2.871, 2.871, 2.871, 2.871, 2.869, 2.869, 2.878, 2.889, 2.894, 2.895, 2.906, 2.914, 2.917, 2.921, + 2.882, 2.879, 2.876, 2.874, 2.871, 2.871, 2.869, 2.869, 2.869, 2.878, 2.891, 2.894, 2.905, 2.914, 2.919, 2.921 + ] + }, + { + "ct": 5500, + "table": + [ + 1.488, 1.488, 1.488, 1.488, 1.491, 1.492, 1.492, 1.491, 1.491, 1.491, 1.492, 1.495, 1.497, 1.499, 1.499, 1.503, + 1.482, 1.485, 1.485, 1.487, 1.489, 1.492, 1.492, 1.492, 1.492, 1.492, 1.494, 1.494, 1.492, 1.491, 1.493, 1.494, + 1.482, 1.482, 1.484, 1.485, 1.487, 1.492, 1.496, 1.498, 1.499, 1.498, 1.494, 1.492, 1.491, 1.491, 1.491, 1.491, + 1.481, 1.481, 1.482, 1.485, 1.491, 1.496, 1.498, 1.499, 1.501, 1.499, 1.498, 1.493, 1.491, 1.488, 1.488, 1.488, + 1.481, 1.481, 1.481, 1.483, 1.491, 1.497, 1.498, 1.499, 1.501, 1.499, 1.498, 1.492, 1.488, 1.485, 1.483, 1.483, + 1.479, 1.479, 1.481, 1.482, 1.489, 1.495, 1.497, 1.498, 1.499, 1.499, 1.495, 1.492, 1.485, 1.482, 1.482, 1.481, + 1.479, 1.479, 1.479, 1.481, 1.489, 1.494, 1.496, 1.497, 1.497, 1.496, 1.495, 1.489, 1.482, 1.481, 1.479, 1.477, + 1.478, 1.478, 1.479, 1.481, 1.487, 1.491, 1.494, 1.496, 1.496, 1.495, 1.492, 1.487, 1.482, 1.479, 1.478, 1.476, + 1.478, 1.478, 1.479, 1.482, 1.486, 1.488, 1.491, 1.493, 1.493, 1.492, 1.487, 1.484, 1.481, 1.479, 1.476, 1.476, + 1.477, 1.479, 1.481, 1.483, 1.485, 1.486, 1.488, 1.488, 1.487, 1.487, 1.484, 1.483, 1.481, 1.479, 1.476, 1.476, + 1.477, 1.479, 1.482, 1.483, 1.484, 1.485, 1.484, 1.482, 1.482, 1.484, 1.483, 1.482, 1.481, 1.479, 1.477, 1.476, + 1.477, 1.479, 1.482, 1.483, 1.484, 1.484, 1.482, 1.482, 1.482, 1.482, 1.482, 1.481, 1.479, 1.479, 1.479, 1.479 + ] + } + ], + "luminance_lut": + [ + 2.764, 2.654, 2.321, 2.043, 1.768, 1.594, 1.558, 1.558, 1.558, 1.568, 1.661, 1.904, 2.193, 2.497, 2.888, 3.043, + 2.654, 2.373, 2.049, 1.819, 1.569, 1.446, 1.381, 1.356, 1.356, 1.403, 1.501, 1.679, 1.939, 2.218, 2.586, 2.888, + 2.376, 2.154, 1.819, 1.569, 1.438, 1.301, 1.246, 1.224, 1.224, 1.263, 1.349, 1.501, 1.679, 1.985, 2.359, 2.609, + 2.267, 1.987, 1.662, 1.438, 1.301, 1.235, 1.132, 1.105, 1.105, 1.164, 1.263, 1.349, 1.528, 1.808, 2.184, 2.491, + 2.218, 1.876, 1.568, 1.367, 1.235, 1.132, 1.087, 1.022, 1.023, 1.104, 1.164, 1.278, 1.439, 1.695, 2.066, 2.429, + 2.218, 1.832, 1.533, 1.341, 1.206, 1.089, 1.013, 1.002, 1.013, 1.026, 1.122, 1.246, 1.399, 1.642, 2.004, 2.426, + 2.218, 1.832, 1.533, 1.341, 1.206, 1.089, 1.011, 1.001, 1.009, 1.026, 1.122, 1.246, 1.399, 1.642, 2.004, 2.426, + 2.224, 1.896, 1.584, 1.382, 1.248, 1.147, 1.088, 1.016, 1.026, 1.118, 1.168, 1.283, 1.444, 1.697, 2.066, 2.428, + 2.292, 2.019, 1.689, 1.462, 1.322, 1.247, 1.147, 1.118, 1.118, 1.168, 1.275, 1.358, 1.532, 1.809, 2.189, 2.491, + 2.444, 2.204, 1.856, 1.606, 1.462, 1.322, 1.257, 1.234, 1.234, 1.275, 1.358, 1.516, 1.686, 1.993, 2.371, 2.622, + 2.748, 2.444, 2.108, 1.856, 1.606, 1.476, 1.399, 1.376, 1.376, 1.422, 1.516, 1.686, 1.968, 2.238, 2.611, 2.935, + 2.862, 2.748, 2.395, 2.099, 1.811, 1.621, 1.582, 1.582, 1.582, 1.592, 1.677, 1.919, 2.223, 2.534, 2.935, 3.078 + ], + "sigma": 0.00428, + "sigma_Cb": 0.00363 + } + }, + { + "rpi.contrast": + { + "ce_enable": 1, + "gamma_curve": + [ + 0, 0, + 1024, 5040, + 2048, 9338, + 3072, 12356, + 4096, 15312, + 5120, 18051, + 6144, 20790, + 7168, 23193, + 8192, 25744, + 9216, 27942, + 10240, 30035, + 11264, 32005, + 12288, 33975, + 13312, 35815, + 14336, 37600, + 15360, 39168, + 16384, 40642, + 18432, 43379, + 20480, 45749, + 22528, 47753, + 24576, 49621, + 26624, 51253, + 28672, 52698, + 30720, 53796, + 32768, 54876, + 36864, 57012, + 40960, 58656, + 45056, 59954, + 49152, 61183, + 53248, 62355, + 57344, 63419, + 61440, 64476, + 65535, 65535 + ] + } + }, + { + "rpi.ccm": + { + "ccms": [ + { + "ct": 2850, + "ccm": + [ + 1.42601, -0.20537, -0.22063, + -0.47682, 1.81987, -0.34305, + 0.01854, -0.86036, 1.84181 + ] + }, + { + "ct": 2900, + "ccm": + [ + 1.29755, 0.04602, -0.34356, + -0.41491, 1.73477, -0.31987, + -0.01345, -0.97115, 1.98459 + ] + }, + { + "ct": 3550, + "ccm": + [ + 1.49811, -0.33412, -0.16398, + -0.40869, 1.72995, -0.32127, + -0.01924, -0.62181, 1.64105 + ] + }, + { + "ct": 4500, + "ccm": + [ + 1.47015, -0.29229, -0.17786, + -0.36561, 1.88919, -0.52358, + -0.03552, -0.56717, 1.60269 + ] + }, + { + "ct": 5600, + "ccm": + [ + 1.60962, -0.47434, -0.13528, + -0.32701, 1.73797, -0.41096, + -0.07626, -0.40171, 1.47796 + ] + }, + { + "ct": 8000, + "ccm": + [ + 1.54642, -0.20396, -0.34246, + -0.31748, 2.22559, -0.90811, + -0.10035, -0.65877, 1.75912 + ] + } + ] + } + }, + { + "rpi.sharpen": { } + } + ] +} \ No newline at end of file diff --git a/src/ipa/rpi/vc4/data/imx477.json b/src/ipa/rpi/vc4/data/imx477.json new file mode 100644 index 00000000..daffc268 --- /dev/null +++ b/src/ipa/rpi/vc4/data/imx477.json @@ -0,0 +1,518 @@ +{ + "version": 2.0, + "target": "bcm2835", + "algorithms": [ + { + "rpi.black_level": + { + "black_level": 4096 + } + }, + { + "rpi.dpc": { } + }, + { + "rpi.lux": + { + "reference_shutter_speed": 27242, + "reference_gain": 1.0, + "reference_aperture": 1.0, + "reference_lux": 830, + "reference_Y": 17755 + } + }, + { + "rpi.noise": + { + "reference_constant": 0, + "reference_slope": 2.767 + } + }, + { + "rpi.geq": + { + "offset": 204, + "slope": 0.01078 + } + }, + { + "rpi.sdn": { } + }, + { + "rpi.awb": + { + "priors": [ + { + "lux": 0, + "prior": + [ + 2000, 1.0, + 3000, 0.0, + 13000, 0.0 + ] + }, + { + "lux": 800, + "prior": + [ + 2000, 0.0, + 6000, 2.0, + 13000, 2.0 + ] + }, + { + "lux": 1500, + "prior": + [ + 2000, 0.0, + 4000, 1.0, + 6000, 6.0, + 6500, 7.0, + 7000, 1.0, + 13000, 1.0 + ] + } + ], + "modes": + { + "auto": + { + "lo": 2500, + "hi": 8000 + }, + "incandescent": + { + "lo": 2500, + "hi": 3000 + }, + "tungsten": + { + "lo": 3000, + "hi": 3500 + }, + "fluorescent": + { + "lo": 4000, + "hi": 4700 + }, + "indoor": + { + "lo": 3000, + "hi": 5000 + }, + "daylight": + { + "lo": 5500, + "hi": 6500 + }, + "cloudy": + { + "lo": 7000, + "hi": 8600 + } + }, + "bayes": 1, + "ct_curve": + [ + 2360.0, 0.6009, 0.3093, + 2848.0, 0.5071, 0.4000, + 2903.0, 0.4905, 0.4392, + 3628.0, 0.4261, 0.5564, + 3643.0, 0.4228, 0.5623, + 4660.0, 0.3529, 0.6800, + 5579.0, 0.3227, 0.7000, + 6125.0, 0.3129, 0.7100, + 6671.0, 0.3065, 0.7200, + 7217.0, 0.3014, 0.7300, + 7763.0, 0.2950, 0.7400, + 9505.0, 0.2524, 0.7856 + ], + "sensitivity_r": 1.05, + "sensitivity_b": 1.05, + "transverse_pos": 0.0238, + "transverse_neg": 0.04429 + } + }, + { + "rpi.agc": + { + "metering_modes": + { + "centre-weighted": + { + "weights": [ 3, 3, 3, 2, 2, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0 ] + }, + "spot": + { + "weights": [ 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ] + }, + "matrix": + { + "weights": [ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ] + } + }, + "exposure_modes": + { + "normal": + { + "shutter": [ 100, 10000, 30000, 60000, 66666 ], + "gain": [ 1.0, 2.0, 4.0, 6.0, 8.0 ] + }, + "short": + { + "shutter": [ 100, 5000, 10000, 20000, 33333 ], + "gain": [ 1.0, 2.0, 4.0, 6.0, 8.0 ] + }, + "long": + { + "shutter": [ 100, 10000, 30000, 60000, 120000 ], + "gain": [ 1.0, 2.0, 4.0, 6.0, 12.0 ] + } + }, + "constraint_modes": + { + "normal": [ + { + "bound": "LOWER", + "q_lo": 0.98, + "q_hi": 1.0, + "y_target": + [ + 0, 0.3, + 1000, 0.3 + ] + } + ], + "highlight": [ + { + "bound": "LOWER", + "q_lo": 0.98, + "q_hi": 1.0, + "y_target": + [ + 0, 0.3, + 1000, 0.3 + ] + }, + { + "bound": "UPPER", + "q_lo": 0.98, + "q_hi": 1.0, + "y_target": + [ + 0, 0.8, + 1000, 0.8 + ] + } + ], + "shadows": [ + { + "bound": "LOWER", + "q_lo": 0.0, + "q_hi": 0.5, + "y_target": + [ + 0, 0.17, + 1000, 0.17 + ] + } + ] + }, + "y_target": + [ + 0, 0.16, + 1000, 0.165, + 10000, 0.17 + ] + } + }, + { + "rpi.alsc": + { + "omega": 1.3, + "n_iter": 100, + "luminance_strength": 0.5, + "calibrations_Cr": [ + { + "ct": 2960, + "table": + [ + 2.088, 2.086, 2.082, 2.081, 2.077, 2.071, 2.068, 2.068, 2.072, 2.073, 2.075, 2.078, 2.084, 2.092, 2.095, 2.098, + 2.086, 2.084, 2.079, 2.078, 2.075, 2.068, 2.064, 2.063, 2.068, 2.071, 2.072, 2.075, 2.081, 2.089, 2.092, 2.094, + 2.083, 2.081, 2.077, 2.072, 2.069, 2.062, 2.059, 2.059, 2.063, 2.067, 2.069, 2.072, 2.079, 2.088, 2.089, 2.089, + 2.081, 2.077, 2.072, 2.068, 2.065, 2.058, 2.055, 2.054, 2.057, 2.062, 2.066, 2.069, 2.077, 2.084, 2.086, 2.086, + 2.078, 2.075, 2.069, 2.065, 2.061, 2.055, 2.052, 2.049, 2.051, 2.056, 2.062, 2.065, 2.072, 2.079, 2.081, 2.079, + 2.079, 2.075, 2.069, 2.064, 2.061, 2.053, 2.049, 2.046, 2.049, 2.051, 2.057, 2.062, 2.069, 2.075, 2.077, 2.075, + 2.082, 2.079, 2.072, 2.065, 2.061, 2.054, 2.049, 2.047, 2.049, 2.051, 2.056, 2.061, 2.066, 2.073, 2.073, 2.069, + 2.086, 2.082, 2.075, 2.068, 2.062, 2.054, 2.051, 2.049, 2.051, 2.052, 2.056, 2.061, 2.066, 2.073, 2.073, 2.072, + 2.088, 2.086, 2.079, 2.074, 2.066, 2.057, 2.051, 2.051, 2.054, 2.055, 2.056, 2.061, 2.067, 2.072, 2.073, 2.072, + 2.091, 2.087, 2.079, 2.075, 2.068, 2.057, 2.052, 2.052, 2.056, 2.055, 2.055, 2.059, 2.066, 2.072, 2.072, 2.072, + 2.093, 2.088, 2.081, 2.077, 2.069, 2.059, 2.054, 2.054, 2.057, 2.056, 2.056, 2.058, 2.066, 2.072, 2.073, 2.073, + 2.095, 2.091, 2.084, 2.078, 2.075, 2.067, 2.057, 2.057, 2.059, 2.059, 2.058, 2.059, 2.068, 2.073, 2.075, 2.078 + ] + }, + { + "ct": 4850, + "table": + [ + 2.973, 2.968, 2.956, 2.943, 2.941, 2.932, 2.923, 2.921, 2.924, 2.929, 2.931, 2.939, 2.953, 2.965, 2.966, 2.976, + 2.969, 2.962, 2.951, 2.941, 2.934, 2.928, 2.919, 2.918, 2.919, 2.923, 2.927, 2.933, 2.945, 2.957, 2.962, 2.962, + 2.964, 2.956, 2.944, 2.932, 2.929, 2.924, 2.915, 2.914, 2.915, 2.919, 2.924, 2.928, 2.941, 2.952, 2.958, 2.959, + 2.957, 2.951, 2.939, 2.928, 2.924, 2.919, 2.913, 2.911, 2.911, 2.915, 2.919, 2.925, 2.936, 2.947, 2.952, 2.953, + 2.954, 2.947, 2.935, 2.924, 2.919, 2.915, 2.908, 2.906, 2.906, 2.907, 2.914, 2.921, 2.932, 2.941, 2.943, 2.942, + 2.953, 2.946, 2.932, 2.921, 2.916, 2.911, 2.904, 2.902, 2.901, 2.904, 2.909, 2.919, 2.926, 2.937, 2.939, 2.939, + 2.953, 2.947, 2.932, 2.918, 2.915, 2.909, 2.903, 2.901, 2.901, 2.906, 2.911, 2.918, 2.924, 2.936, 2.936, 2.932, + 2.956, 2.948, 2.934, 2.919, 2.916, 2.908, 2.903, 2.901, 2.902, 2.907, 2.909, 2.917, 2.926, 2.936, 2.939, 2.939, + 2.957, 2.951, 2.936, 2.923, 2.917, 2.907, 2.904, 2.901, 2.902, 2.908, 2.911, 2.919, 2.929, 2.939, 2.942, 2.942, + 2.961, 2.951, 2.936, 2.922, 2.918, 2.906, 2.904, 2.901, 2.901, 2.907, 2.911, 2.921, 2.931, 2.941, 2.942, 2.944, + 2.964, 2.954, 2.936, 2.924, 2.918, 2.909, 2.905, 2.905, 2.905, 2.907, 2.912, 2.923, 2.933, 2.942, 2.944, 2.944, + 2.964, 2.958, 2.943, 2.927, 2.921, 2.914, 2.909, 2.907, 2.907, 2.912, 2.916, 2.928, 2.936, 2.944, 2.947, 2.952 + ] + }, + { + "ct": 5930, + "table": + [ + 3.312, 3.308, 3.301, 3.294, 3.288, 3.277, 3.268, 3.261, 3.259, 3.261, 3.267, 3.273, 3.285, 3.301, 3.303, 3.312, + 3.308, 3.304, 3.294, 3.291, 3.283, 3.271, 3.263, 3.259, 3.257, 3.258, 3.261, 3.268, 3.278, 3.293, 3.299, 3.299, + 3.302, 3.296, 3.288, 3.282, 3.276, 3.267, 3.259, 3.254, 3.252, 3.253, 3.256, 3.261, 3.273, 3.289, 3.292, 3.292, + 3.296, 3.289, 3.282, 3.276, 3.269, 3.263, 3.256, 3.251, 3.248, 3.249, 3.251, 3.257, 3.268, 3.279, 3.284, 3.284, + 3.292, 3.285, 3.279, 3.271, 3.264, 3.257, 3.249, 3.243, 3.241, 3.241, 3.246, 3.252, 3.261, 3.274, 3.275, 3.273, + 3.291, 3.285, 3.276, 3.268, 3.259, 3.251, 3.242, 3.239, 3.236, 3.238, 3.244, 3.248, 3.258, 3.268, 3.269, 3.265, + 3.294, 3.288, 3.275, 3.266, 3.257, 3.248, 3.239, 3.238, 3.237, 3.238, 3.243, 3.246, 3.255, 3.264, 3.264, 3.257, + 3.297, 3.293, 3.279, 3.268, 3.258, 3.249, 3.238, 3.237, 3.239, 3.239, 3.243, 3.245, 3.255, 3.264, 3.264, 3.263, + 3.301, 3.295, 3.281, 3.271, 3.259, 3.248, 3.237, 3.237, 3.239, 3.241, 3.243, 3.246, 3.257, 3.265, 3.266, 3.264, + 3.306, 3.295, 3.279, 3.271, 3.261, 3.247, 3.235, 3.234, 3.239, 3.239, 3.243, 3.247, 3.258, 3.265, 3.265, 3.264, + 3.308, 3.297, 3.279, 3.272, 3.261, 3.249, 3.239, 3.239, 3.241, 3.243, 3.245, 3.248, 3.261, 3.265, 3.266, 3.265, + 3.309, 3.301, 3.286, 3.276, 3.267, 3.256, 3.246, 3.242, 3.244, 3.244, 3.249, 3.253, 3.263, 3.267, 3.271, 3.274 + ] + } + ], + "calibrations_Cb": [ + { + "ct": 2960, + "table": + [ + 2.133, 2.134, 2.139, 2.143, 2.148, 2.155, 2.158, 2.158, 2.158, 2.161, 2.161, 2.162, 2.159, 2.156, 2.152, 2.151, + 2.132, 2.133, 2.135, 2.142, 2.147, 2.153, 2.158, 2.158, 2.158, 2.158, 2.159, 2.159, 2.157, 2.154, 2.151, 2.148, + 2.133, 2.133, 2.135, 2.142, 2.149, 2.154, 2.158, 2.158, 2.157, 2.156, 2.158, 2.157, 2.155, 2.153, 2.148, 2.146, + 2.133, 2.133, 2.138, 2.145, 2.149, 2.154, 2.158, 2.159, 2.158, 2.155, 2.157, 2.156, 2.153, 2.149, 2.146, 2.144, + 2.133, 2.134, 2.139, 2.146, 2.149, 2.154, 2.158, 2.159, 2.159, 2.156, 2.154, 2.154, 2.149, 2.145, 2.143, 2.139, + 2.135, 2.135, 2.139, 2.146, 2.151, 2.155, 2.158, 2.159, 2.158, 2.156, 2.153, 2.151, 2.146, 2.143, 2.139, 2.136, + 2.135, 2.135, 2.138, 2.145, 2.151, 2.154, 2.157, 2.158, 2.157, 2.156, 2.153, 2.151, 2.147, 2.143, 2.141, 2.137, + 2.135, 2.134, 2.135, 2.141, 2.149, 2.154, 2.157, 2.157, 2.157, 2.157, 2.157, 2.153, 2.149, 2.146, 2.142, 2.139, + 2.132, 2.133, 2.135, 2.139, 2.148, 2.153, 2.158, 2.159, 2.159, 2.161, 2.161, 2.157, 2.154, 2.149, 2.144, 2.141, + 2.132, 2.133, 2.135, 2.141, 2.149, 2.155, 2.161, 2.161, 2.162, 2.162, 2.163, 2.159, 2.154, 2.149, 2.144, 2.138, + 2.136, 2.136, 2.137, 2.143, 2.149, 2.156, 2.162, 2.163, 2.162, 2.163, 2.164, 2.161, 2.157, 2.152, 2.146, 2.138, + 2.137, 2.137, 2.141, 2.147, 2.152, 2.157, 2.162, 2.162, 2.159, 2.161, 2.162, 2.162, 2.157, 2.152, 2.148, 2.148 + ] + }, + { + "ct": 4850, + "table": + [ + 1.463, 1.464, 1.471, 1.478, 1.479, 1.483, 1.484, 1.486, 1.486, 1.484, 1.483, 1.481, 1.478, 1.475, 1.471, 1.468, + 1.463, 1.463, 1.468, 1.476, 1.479, 1.482, 1.484, 1.487, 1.486, 1.484, 1.483, 1.482, 1.478, 1.473, 1.469, 1.468, + 1.463, 1.464, 1.468, 1.476, 1.479, 1.483, 1.484, 1.486, 1.486, 1.485, 1.484, 1.482, 1.477, 1.473, 1.469, 1.468, + 1.463, 1.464, 1.469, 1.477, 1.481, 1.483, 1.485, 1.487, 1.487, 1.485, 1.485, 1.482, 1.478, 1.474, 1.469, 1.468, + 1.465, 1.465, 1.471, 1.478, 1.481, 1.484, 1.486, 1.488, 1.488, 1.487, 1.485, 1.482, 1.477, 1.472, 1.468, 1.467, + 1.465, 1.466, 1.472, 1.479, 1.482, 1.485, 1.486, 1.488, 1.488, 1.486, 1.484, 1.479, 1.475, 1.472, 1.468, 1.466, + 1.466, 1.466, 1.472, 1.478, 1.482, 1.484, 1.485, 1.488, 1.487, 1.485, 1.483, 1.479, 1.475, 1.472, 1.469, 1.468, + 1.465, 1.466, 1.469, 1.476, 1.481, 1.485, 1.485, 1.486, 1.486, 1.485, 1.483, 1.479, 1.477, 1.474, 1.471, 1.469, + 1.464, 1.465, 1.469, 1.476, 1.481, 1.484, 1.485, 1.487, 1.487, 1.486, 1.485, 1.481, 1.478, 1.475, 1.471, 1.469, + 1.463, 1.464, 1.469, 1.477, 1.481, 1.485, 1.485, 1.488, 1.488, 1.487, 1.486, 1.481, 1.478, 1.475, 1.471, 1.468, + 1.464, 1.465, 1.471, 1.478, 1.482, 1.486, 1.486, 1.488, 1.488, 1.487, 1.486, 1.481, 1.478, 1.475, 1.472, 1.468, + 1.465, 1.466, 1.472, 1.481, 1.483, 1.487, 1.487, 1.488, 1.488, 1.486, 1.485, 1.481, 1.479, 1.476, 1.473, 1.472 + ] + }, + { + "ct": 5930, + "table": + [ + 1.443, 1.444, 1.448, 1.453, 1.459, 1.463, 1.465, 1.467, 1.469, 1.469, 1.467, 1.466, 1.462, 1.457, 1.454, 1.451, + 1.443, 1.444, 1.445, 1.451, 1.459, 1.463, 1.465, 1.467, 1.469, 1.469, 1.467, 1.465, 1.461, 1.456, 1.452, 1.451, + 1.444, 1.444, 1.445, 1.451, 1.459, 1.463, 1.466, 1.468, 1.469, 1.469, 1.467, 1.465, 1.461, 1.456, 1.452, 1.449, + 1.444, 1.444, 1.447, 1.452, 1.459, 1.464, 1.467, 1.469, 1.471, 1.469, 1.467, 1.466, 1.461, 1.456, 1.452, 1.449, + 1.444, 1.445, 1.448, 1.452, 1.459, 1.465, 1.469, 1.471, 1.471, 1.471, 1.468, 1.465, 1.461, 1.455, 1.451, 1.449, + 1.445, 1.446, 1.449, 1.453, 1.461, 1.466, 1.469, 1.471, 1.472, 1.469, 1.467, 1.465, 1.459, 1.455, 1.451, 1.447, + 1.446, 1.446, 1.449, 1.453, 1.461, 1.466, 1.469, 1.469, 1.469, 1.469, 1.467, 1.465, 1.459, 1.455, 1.452, 1.449, + 1.446, 1.446, 1.447, 1.451, 1.459, 1.466, 1.469, 1.469, 1.469, 1.469, 1.467, 1.465, 1.461, 1.457, 1.454, 1.451, + 1.444, 1.444, 1.447, 1.451, 1.459, 1.466, 1.469, 1.469, 1.471, 1.471, 1.468, 1.466, 1.462, 1.458, 1.454, 1.452, + 1.444, 1.444, 1.448, 1.453, 1.459, 1.466, 1.469, 1.471, 1.472, 1.472, 1.468, 1.466, 1.462, 1.458, 1.454, 1.449, + 1.446, 1.447, 1.449, 1.454, 1.461, 1.466, 1.471, 1.471, 1.471, 1.471, 1.468, 1.466, 1.462, 1.459, 1.455, 1.449, + 1.447, 1.447, 1.452, 1.457, 1.462, 1.468, 1.472, 1.472, 1.471, 1.471, 1.468, 1.466, 1.462, 1.459, 1.456, 1.455 + ] + } + ], + "luminance_lut": + [ + 1.548, 1.499, 1.387, 1.289, 1.223, 1.183, 1.164, 1.154, 1.153, 1.169, 1.211, 1.265, 1.345, 1.448, 1.581, 1.619, + 1.513, 1.412, 1.307, 1.228, 1.169, 1.129, 1.105, 1.098, 1.103, 1.127, 1.157, 1.209, 1.272, 1.361, 1.481, 1.583, + 1.449, 1.365, 1.257, 1.175, 1.124, 1.085, 1.062, 1.054, 1.059, 1.079, 1.113, 1.151, 1.211, 1.293, 1.407, 1.488, + 1.424, 1.324, 1.222, 1.139, 1.089, 1.056, 1.034, 1.031, 1.034, 1.049, 1.075, 1.115, 1.164, 1.241, 1.351, 1.446, + 1.412, 1.297, 1.203, 1.119, 1.069, 1.039, 1.021, 1.016, 1.022, 1.032, 1.052, 1.086, 1.135, 1.212, 1.321, 1.439, + 1.406, 1.287, 1.195, 1.115, 1.059, 1.028, 1.014, 1.012, 1.015, 1.026, 1.041, 1.074, 1.125, 1.201, 1.302, 1.425, + 1.406, 1.294, 1.205, 1.126, 1.062, 1.031, 1.013, 1.009, 1.011, 1.019, 1.042, 1.079, 1.129, 1.203, 1.302, 1.435, + 1.415, 1.318, 1.229, 1.146, 1.076, 1.039, 1.019, 1.014, 1.017, 1.031, 1.053, 1.093, 1.144, 1.219, 1.314, 1.436, + 1.435, 1.348, 1.246, 1.164, 1.094, 1.059, 1.036, 1.032, 1.037, 1.049, 1.072, 1.114, 1.167, 1.257, 1.343, 1.462, + 1.471, 1.385, 1.278, 1.189, 1.124, 1.084, 1.064, 1.061, 1.069, 1.078, 1.101, 1.146, 1.207, 1.298, 1.415, 1.496, + 1.522, 1.436, 1.323, 1.228, 1.169, 1.118, 1.101, 1.094, 1.099, 1.113, 1.146, 1.194, 1.265, 1.353, 1.474, 1.571, + 1.578, 1.506, 1.378, 1.281, 1.211, 1.156, 1.135, 1.134, 1.139, 1.158, 1.194, 1.251, 1.327, 1.427, 1.559, 1.611 + ], + "sigma": 0.00121, + "sigma_Cb": 0.00115 + } + }, + { + "rpi.contrast": + { + "ce_enable": 1, + "gamma_curve": + [ + 0, 0, + 1024, 5040, + 2048, 9338, + 3072, 12356, + 4096, 15312, + 5120, 18051, + 6144, 20790, + 7168, 23193, + 8192, 25744, + 9216, 27942, + 10240, 30035, + 11264, 32005, + 12288, 33975, + 13312, 35815, + 14336, 37600, + 15360, 39168, + 16384, 40642, + 18432, 43379, + 20480, 45749, + 22528, 47753, + 24576, 49621, + 26624, 51253, + 28672, 52698, + 30720, 53796, + 32768, 54876, + 36864, 57012, + 40960, 58656, + 45056, 59954, + 49152, 61183, + 53248, 62355, + 57344, 63419, + 61440, 64476, + 65535, 65535 + ] + } + }, + { + "rpi.ccm": + { + "ccms": [ + { + "ct": 2360, + "ccm": + [ + 1.66078, -0.23588, -0.42491, + -0.47456, 1.82763, -0.35307, + -0.00545, -1.44729, 2.45273 + ] + }, + { + "ct": 2870, + "ccm": + [ + 1.78373, -0.55344, -0.23029, + -0.39951, 1.69701, -0.29751, + 0.01986, -1.06525, 2.04539 + ] + }, + { + "ct": 2970, + "ccm": + [ + 1.73511, -0.56973, -0.16537, + -0.36338, 1.69878, -0.33539, + -0.02354, -0.76813, 1.79168 + ] + }, + { + "ct": 3000, + "ccm": + [ + 2.06374, -0.92218, -0.14156, + -0.41721, 1.69289, -0.27568, + -0.00554, -0.92741, 1.93295 + ] + }, + { + "ct": 3700, + "ccm": + [ + 2.13792, -1.08136, -0.05655, + -0.34739, 1.58989, -0.24249, + -0.00349, -0.76789, 1.77138 + ] + }, + { + "ct": 3870, + "ccm": + [ + 1.83834, -0.70528, -0.13307, + -0.30499, 1.60523, -0.30024, + -0.05701, -0.58313, 1.64014 + ] + }, + { + "ct": 4000, + "ccm": + [ + 2.15741, -1.10295, -0.05447, + -0.34631, 1.61158, -0.26528, + -0.02723, -0.70288, 1.73011 + ] + }, + { + "ct": 4400, + "ccm": + [ + 2.05729, -0.95007, -0.10723, + -0.41712, 1.78606, -0.36894, + -0.11899, -0.55727, 1.67626 + ] + }, + { + "ct": 4715, + "ccm": + [ + 1.90255, -0.77478, -0.12777, + -0.31338, 1.88197, -0.56858, + -0.06001, -0.61785, 1.67786 + ] + }, + { + "ct": 5920, + "ccm": + [ + 1.98691, -0.84671, -0.14019, + -0.26581, 1.70615, -0.44035, + -0.09532, -0.47332, 1.56864 + ] + }, + { + "ct": 9050, + "ccm": + [ + 2.09255, -0.76541, -0.32714, + -0.28973, 2.27462, -0.98489, + -0.17299, -0.61275, 1.78574 + ] + } + ] + } + }, + { + "rpi.sharpen": { } + } + ] +} \ No newline at end of file diff --git a/src/ipa/rpi/vc4/data/imx477_noir.json b/src/ipa/rpi/vc4/data/imx477_noir.json new file mode 100644 index 00000000..52d7f072 --- /dev/null +++ b/src/ipa/rpi/vc4/data/imx477_noir.json @@ -0,0 +1,429 @@ +{ + "version": 2.0, + "target": "bcm2835", + "algorithms": [ + { + "rpi.black_level": + { + "black_level": 4096 + } + }, + { + "rpi.dpc": { } + }, + { + "rpi.lux": + { + "reference_shutter_speed": 27242, + "reference_gain": 1.0, + "reference_aperture": 1.0, + "reference_lux": 830, + "reference_Y": 17755 + } + }, + { + "rpi.noise": + { + "reference_constant": 0, + "reference_slope": 2.767 + } + }, + { + "rpi.geq": + { + "offset": 204, + "slope": 0.01078 + } + }, + { + "rpi.sdn": { } + }, + { + "rpi.awb": + { + "bayes": 0 + } + }, + { + "rpi.agc": + { + "metering_modes": + { + "centre-weighted": + { + "weights": [ 3, 3, 3, 2, 2, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0 ] + }, + "spot": + { + "weights": [ 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ] + }, + "matrix": + { + "weights": [ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ] + } + }, + "exposure_modes": + { + "normal": + { + "shutter": [ 100, 10000, 30000, 60000, 66666 ], + "gain": [ 1.0, 2.0, 4.0, 6.0, 8.0 ] + }, + "short": + { + "shutter": [ 100, 5000, 10000, 20000, 33333 ], + "gain": [ 1.0, 2.0, 4.0, 6.0, 8.0 ] + }, + "long": + { + "shutter": [ 100, 10000, 30000, 60000, 120000 ], + "gain": [ 1.0, 2.0, 4.0, 6.0, 12.0 ] + } + }, + "constraint_modes": + { + "normal": [ + { + "bound": "LOWER", + "q_lo": 0.98, + "q_hi": 1.0, + "y_target": + [ + 0, 0.3, + 1000, 0.3 + ] + } + ], + "highlight": [ + { + "bound": "LOWER", + "q_lo": 0.98, + "q_hi": 1.0, + "y_target": + [ + 0, 0.3, + 1000, 0.3 + ] + }, + { + "bound": "UPPER", + "q_lo": 0.98, + "q_hi": 1.0, + "y_target": + [ + 0, 0.8, + 1000, 0.8 + ] + } + ], + "shadows": [ + { + "bound": "LOWER", + "q_lo": 0.0, + "q_hi": 0.5, + "y_target": + [ + 0, 0.17, + 1000, 0.17 + ] + } + ] + }, + "y_target": + [ + 0, 0.16, + 1000, 0.165, + 10000, 0.17 + ] + } + }, + { + "rpi.alsc": + { + "omega": 1.3, + "n_iter": 100, + "luminance_strength": 0.5, + "calibrations_Cr": [ + { + "ct": 2960, + "table": + [ + 2.088, 2.086, 2.082, 2.081, 2.077, 2.071, 2.068, 2.068, 2.072, 2.073, 2.075, 2.078, 2.084, 2.092, 2.095, 2.098, + 2.086, 2.084, 2.079, 2.078, 2.075, 2.068, 2.064, 2.063, 2.068, 2.071, 2.072, 2.075, 2.081, 2.089, 2.092, 2.094, + 2.083, 2.081, 2.077, 2.072, 2.069, 2.062, 2.059, 2.059, 2.063, 2.067, 2.069, 2.072, 2.079, 2.088, 2.089, 2.089, + 2.081, 2.077, 2.072, 2.068, 2.065, 2.058, 2.055, 2.054, 2.057, 2.062, 2.066, 2.069, 2.077, 2.084, 2.086, 2.086, + 2.078, 2.075, 2.069, 2.065, 2.061, 2.055, 2.052, 2.049, 2.051, 2.056, 2.062, 2.065, 2.072, 2.079, 2.081, 2.079, + 2.079, 2.075, 2.069, 2.064, 2.061, 2.053, 2.049, 2.046, 2.049, 2.051, 2.057, 2.062, 2.069, 2.075, 2.077, 2.075, + 2.082, 2.079, 2.072, 2.065, 2.061, 2.054, 2.049, 2.047, 2.049, 2.051, 2.056, 2.061, 2.066, 2.073, 2.073, 2.069, + 2.086, 2.082, 2.075, 2.068, 2.062, 2.054, 2.051, 2.049, 2.051, 2.052, 2.056, 2.061, 2.066, 2.073, 2.073, 2.072, + 2.088, 2.086, 2.079, 2.074, 2.066, 2.057, 2.051, 2.051, 2.054, 2.055, 2.056, 2.061, 2.067, 2.072, 2.073, 2.072, + 2.091, 2.087, 2.079, 2.075, 2.068, 2.057, 2.052, 2.052, 2.056, 2.055, 2.055, 2.059, 2.066, 2.072, 2.072, 2.072, + 2.093, 2.088, 2.081, 2.077, 2.069, 2.059, 2.054, 2.054, 2.057, 2.056, 2.056, 2.058, 2.066, 2.072, 2.073, 2.073, + 2.095, 2.091, 2.084, 2.078, 2.075, 2.067, 2.057, 2.057, 2.059, 2.059, 2.058, 2.059, 2.068, 2.073, 2.075, 2.078 + ] + }, + { + "ct": 4850, + "table": + [ + 2.973, 2.968, 2.956, 2.943, 2.941, 2.932, 2.923, 2.921, 2.924, 2.929, 2.931, 2.939, 2.953, 2.965, 2.966, 2.976, + 2.969, 2.962, 2.951, 2.941, 2.934, 2.928, 2.919, 2.918, 2.919, 2.923, 2.927, 2.933, 2.945, 2.957, 2.962, 2.962, + 2.964, 2.956, 2.944, 2.932, 2.929, 2.924, 2.915, 2.914, 2.915, 2.919, 2.924, 2.928, 2.941, 2.952, 2.958, 2.959, + 2.957, 2.951, 2.939, 2.928, 2.924, 2.919, 2.913, 2.911, 2.911, 2.915, 2.919, 2.925, 2.936, 2.947, 2.952, 2.953, + 2.954, 2.947, 2.935, 2.924, 2.919, 2.915, 2.908, 2.906, 2.906, 2.907, 2.914, 2.921, 2.932, 2.941, 2.943, 2.942, + 2.953, 2.946, 2.932, 2.921, 2.916, 2.911, 2.904, 2.902, 2.901, 2.904, 2.909, 2.919, 2.926, 2.937, 2.939, 2.939, + 2.953, 2.947, 2.932, 2.918, 2.915, 2.909, 2.903, 2.901, 2.901, 2.906, 2.911, 2.918, 2.924, 2.936, 2.936, 2.932, + 2.956, 2.948, 2.934, 2.919, 2.916, 2.908, 2.903, 2.901, 2.902, 2.907, 2.909, 2.917, 2.926, 2.936, 2.939, 2.939, + 2.957, 2.951, 2.936, 2.923, 2.917, 2.907, 2.904, 2.901, 2.902, 2.908, 2.911, 2.919, 2.929, 2.939, 2.942, 2.942, + 2.961, 2.951, 2.936, 2.922, 2.918, 2.906, 2.904, 2.901, 2.901, 2.907, 2.911, 2.921, 2.931, 2.941, 2.942, 2.944, + 2.964, 2.954, 2.936, 2.924, 2.918, 2.909, 2.905, 2.905, 2.905, 2.907, 2.912, 2.923, 2.933, 2.942, 2.944, 2.944, + 2.964, 2.958, 2.943, 2.927, 2.921, 2.914, 2.909, 2.907, 2.907, 2.912, 2.916, 2.928, 2.936, 2.944, 2.947, 2.952 + ] + }, + { + "ct": 5930, + "table": + [ + 3.312, 3.308, 3.301, 3.294, 3.288, 3.277, 3.268, 3.261, 3.259, 3.261, 3.267, 3.273, 3.285, 3.301, 3.303, 3.312, + 3.308, 3.304, 3.294, 3.291, 3.283, 3.271, 3.263, 3.259, 3.257, 3.258, 3.261, 3.268, 3.278, 3.293, 3.299, 3.299, + 3.302, 3.296, 3.288, 3.282, 3.276, 3.267, 3.259, 3.254, 3.252, 3.253, 3.256, 3.261, 3.273, 3.289, 3.292, 3.292, + 3.296, 3.289, 3.282, 3.276, 3.269, 3.263, 3.256, 3.251, 3.248, 3.249, 3.251, 3.257, 3.268, 3.279, 3.284, 3.284, + 3.292, 3.285, 3.279, 3.271, 3.264, 3.257, 3.249, 3.243, 3.241, 3.241, 3.246, 3.252, 3.261, 3.274, 3.275, 3.273, + 3.291, 3.285, 3.276, 3.268, 3.259, 3.251, 3.242, 3.239, 3.236, 3.238, 3.244, 3.248, 3.258, 3.268, 3.269, 3.265, + 3.294, 3.288, 3.275, 3.266, 3.257, 3.248, 3.239, 3.238, 3.237, 3.238, 3.243, 3.246, 3.255, 3.264, 3.264, 3.257, + 3.297, 3.293, 3.279, 3.268, 3.258, 3.249, 3.238, 3.237, 3.239, 3.239, 3.243, 3.245, 3.255, 3.264, 3.264, 3.263, + 3.301, 3.295, 3.281, 3.271, 3.259, 3.248, 3.237, 3.237, 3.239, 3.241, 3.243, 3.246, 3.257, 3.265, 3.266, 3.264, + 3.306, 3.295, 3.279, 3.271, 3.261, 3.247, 3.235, 3.234, 3.239, 3.239, 3.243, 3.247, 3.258, 3.265, 3.265, 3.264, + 3.308, 3.297, 3.279, 3.272, 3.261, 3.249, 3.239, 3.239, 3.241, 3.243, 3.245, 3.248, 3.261, 3.265, 3.266, 3.265, + 3.309, 3.301, 3.286, 3.276, 3.267, 3.256, 3.246, 3.242, 3.244, 3.244, 3.249, 3.253, 3.263, 3.267, 3.271, 3.274 + ] + } + ], + "calibrations_Cb": [ + { + "ct": 2960, + "table": + [ + 2.133, 2.134, 2.139, 2.143, 2.148, 2.155, 2.158, 2.158, 2.158, 2.161, 2.161, 2.162, 2.159, 2.156, 2.152, 2.151, + 2.132, 2.133, 2.135, 2.142, 2.147, 2.153, 2.158, 2.158, 2.158, 2.158, 2.159, 2.159, 2.157, 2.154, 2.151, 2.148, + 2.133, 2.133, 2.135, 2.142, 2.149, 2.154, 2.158, 2.158, 2.157, 2.156, 2.158, 2.157, 2.155, 2.153, 2.148, 2.146, + 2.133, 2.133, 2.138, 2.145, 2.149, 2.154, 2.158, 2.159, 2.158, 2.155, 2.157, 2.156, 2.153, 2.149, 2.146, 2.144, + 2.133, 2.134, 2.139, 2.146, 2.149, 2.154, 2.158, 2.159, 2.159, 2.156, 2.154, 2.154, 2.149, 2.145, 2.143, 2.139, + 2.135, 2.135, 2.139, 2.146, 2.151, 2.155, 2.158, 2.159, 2.158, 2.156, 2.153, 2.151, 2.146, 2.143, 2.139, 2.136, + 2.135, 2.135, 2.138, 2.145, 2.151, 2.154, 2.157, 2.158, 2.157, 2.156, 2.153, 2.151, 2.147, 2.143, 2.141, 2.137, + 2.135, 2.134, 2.135, 2.141, 2.149, 2.154, 2.157, 2.157, 2.157, 2.157, 2.157, 2.153, 2.149, 2.146, 2.142, 2.139, + 2.132, 2.133, 2.135, 2.139, 2.148, 2.153, 2.158, 2.159, 2.159, 2.161, 2.161, 2.157, 2.154, 2.149, 2.144, 2.141, + 2.132, 2.133, 2.135, 2.141, 2.149, 2.155, 2.161, 2.161, 2.162, 2.162, 2.163, 2.159, 2.154, 2.149, 2.144, 2.138, + 2.136, 2.136, 2.137, 2.143, 2.149, 2.156, 2.162, 2.163, 2.162, 2.163, 2.164, 2.161, 2.157, 2.152, 2.146, 2.138, + 2.137, 2.137, 2.141, 2.147, 2.152, 2.157, 2.162, 2.162, 2.159, 2.161, 2.162, 2.162, 2.157, 2.152, 2.148, 2.148 + ] + }, + { + "ct": 4850, + "table": + [ + 1.463, 1.464, 1.471, 1.478, 1.479, 1.483, 1.484, 1.486, 1.486, 1.484, 1.483, 1.481, 1.478, 1.475, 1.471, 1.468, + 1.463, 1.463, 1.468, 1.476, 1.479, 1.482, 1.484, 1.487, 1.486, 1.484, 1.483, 1.482, 1.478, 1.473, 1.469, 1.468, + 1.463, 1.464, 1.468, 1.476, 1.479, 1.483, 1.484, 1.486, 1.486, 1.485, 1.484, 1.482, 1.477, 1.473, 1.469, 1.468, + 1.463, 1.464, 1.469, 1.477, 1.481, 1.483, 1.485, 1.487, 1.487, 1.485, 1.485, 1.482, 1.478, 1.474, 1.469, 1.468, + 1.465, 1.465, 1.471, 1.478, 1.481, 1.484, 1.486, 1.488, 1.488, 1.487, 1.485, 1.482, 1.477, 1.472, 1.468, 1.467, + 1.465, 1.466, 1.472, 1.479, 1.482, 1.485, 1.486, 1.488, 1.488, 1.486, 1.484, 1.479, 1.475, 1.472, 1.468, 1.466, + 1.466, 1.466, 1.472, 1.478, 1.482, 1.484, 1.485, 1.488, 1.487, 1.485, 1.483, 1.479, 1.475, 1.472, 1.469, 1.468, + 1.465, 1.466, 1.469, 1.476, 1.481, 1.485, 1.485, 1.486, 1.486, 1.485, 1.483, 1.479, 1.477, 1.474, 1.471, 1.469, + 1.464, 1.465, 1.469, 1.476, 1.481, 1.484, 1.485, 1.487, 1.487, 1.486, 1.485, 1.481, 1.478, 1.475, 1.471, 1.469, + 1.463, 1.464, 1.469, 1.477, 1.481, 1.485, 1.485, 1.488, 1.488, 1.487, 1.486, 1.481, 1.478, 1.475, 1.471, 1.468, + 1.464, 1.465, 1.471, 1.478, 1.482, 1.486, 1.486, 1.488, 1.488, 1.487, 1.486, 1.481, 1.478, 1.475, 1.472, 1.468, + 1.465, 1.466, 1.472, 1.481, 1.483, 1.487, 1.487, 1.488, 1.488, 1.486, 1.485, 1.481, 1.479, 1.476, 1.473, 1.472 + ] + }, + { + "ct": 5930, + "table": + [ + 1.443, 1.444, 1.448, 1.453, 1.459, 1.463, 1.465, 1.467, 1.469, 1.469, 1.467, 1.466, 1.462, 1.457, 1.454, 1.451, + 1.443, 1.444, 1.445, 1.451, 1.459, 1.463, 1.465, 1.467, 1.469, 1.469, 1.467, 1.465, 1.461, 1.456, 1.452, 1.451, + 1.444, 1.444, 1.445, 1.451, 1.459, 1.463, 1.466, 1.468, 1.469, 1.469, 1.467, 1.465, 1.461, 1.456, 1.452, 1.449, + 1.444, 1.444, 1.447, 1.452, 1.459, 1.464, 1.467, 1.469, 1.471, 1.469, 1.467, 1.466, 1.461, 1.456, 1.452, 1.449, + 1.444, 1.445, 1.448, 1.452, 1.459, 1.465, 1.469, 1.471, 1.471, 1.471, 1.468, 1.465, 1.461, 1.455, 1.451, 1.449, + 1.445, 1.446, 1.449, 1.453, 1.461, 1.466, 1.469, 1.471, 1.472, 1.469, 1.467, 1.465, 1.459, 1.455, 1.451, 1.447, + 1.446, 1.446, 1.449, 1.453, 1.461, 1.466, 1.469, 1.469, 1.469, 1.469, 1.467, 1.465, 1.459, 1.455, 1.452, 1.449, + 1.446, 1.446, 1.447, 1.451, 1.459, 1.466, 1.469, 1.469, 1.469, 1.469, 1.467, 1.465, 1.461, 1.457, 1.454, 1.451, + 1.444, 1.444, 1.447, 1.451, 1.459, 1.466, 1.469, 1.469, 1.471, 1.471, 1.468, 1.466, 1.462, 1.458, 1.454, 1.452, + 1.444, 1.444, 1.448, 1.453, 1.459, 1.466, 1.469, 1.471, 1.472, 1.472, 1.468, 1.466, 1.462, 1.458, 1.454, 1.449, + 1.446, 1.447, 1.449, 1.454, 1.461, 1.466, 1.471, 1.471, 1.471, 1.471, 1.468, 1.466, 1.462, 1.459, 1.455, 1.449, + 1.447, 1.447, 1.452, 1.457, 1.462, 1.468, 1.472, 1.472, 1.471, 1.471, 1.468, 1.466, 1.462, 1.459, 1.456, 1.455 + ] + } + ], + "luminance_lut": + [ + 1.548, 1.499, 1.387, 1.289, 1.223, 1.183, 1.164, 1.154, 1.153, 1.169, 1.211, 1.265, 1.345, 1.448, 1.581, 1.619, + 1.513, 1.412, 1.307, 1.228, 1.169, 1.129, 1.105, 1.098, 1.103, 1.127, 1.157, 1.209, 1.272, 1.361, 1.481, 1.583, + 1.449, 1.365, 1.257, 1.175, 1.124, 1.085, 1.062, 1.054, 1.059, 1.079, 1.113, 1.151, 1.211, 1.293, 1.407, 1.488, + 1.424, 1.324, 1.222, 1.139, 1.089, 1.056, 1.034, 1.031, 1.034, 1.049, 1.075, 1.115, 1.164, 1.241, 1.351, 1.446, + 1.412, 1.297, 1.203, 1.119, 1.069, 1.039, 1.021, 1.016, 1.022, 1.032, 1.052, 1.086, 1.135, 1.212, 1.321, 1.439, + 1.406, 1.287, 1.195, 1.115, 1.059, 1.028, 1.014, 1.012, 1.015, 1.026, 1.041, 1.074, 1.125, 1.201, 1.302, 1.425, + 1.406, 1.294, 1.205, 1.126, 1.062, 1.031, 1.013, 1.009, 1.011, 1.019, 1.042, 1.079, 1.129, 1.203, 1.302, 1.435, + 1.415, 1.318, 1.229, 1.146, 1.076, 1.039, 1.019, 1.014, 1.017, 1.031, 1.053, 1.093, 1.144, 1.219, 1.314, 1.436, + 1.435, 1.348, 1.246, 1.164, 1.094, 1.059, 1.036, 1.032, 1.037, 1.049, 1.072, 1.114, 1.167, 1.257, 1.343, 1.462, + 1.471, 1.385, 1.278, 1.189, 1.124, 1.084, 1.064, 1.061, 1.069, 1.078, 1.101, 1.146, 1.207, 1.298, 1.415, 1.496, + 1.522, 1.436, 1.323, 1.228, 1.169, 1.118, 1.101, 1.094, 1.099, 1.113, 1.146, 1.194, 1.265, 1.353, 1.474, 1.571, + 1.578, 1.506, 1.378, 1.281, 1.211, 1.156, 1.135, 1.134, 1.139, 1.158, 1.194, 1.251, 1.327, 1.427, 1.559, 1.611 + ], + "sigma": 0.00121, + "sigma_Cb": 0.00115 + } + }, + { + "rpi.contrast": + { + "ce_enable": 1, + "gamma_curve": + [ + 0, 0, + 1024, 5040, + 2048, 9338, + 3072, 12356, + 4096, 15312, + 5120, 18051, + 6144, 20790, + 7168, 23193, + 8192, 25744, + 9216, 27942, + 10240, 30035, + 11264, 32005, + 12288, 33975, + 13312, 35815, + 14336, 37600, + 15360, 39168, + 16384, 40642, + 18432, 43379, + 20480, 45749, + 22528, 47753, + 24576, 49621, + 26624, 51253, + 28672, 52698, + 30720, 53796, + 32768, 54876, + 36864, 57012, + 40960, 58656, + 45056, 59954, + 49152, 61183, + 53248, 62355, + 57344, 63419, + 61440, 64476, + 65535, 65535 + ] + } + }, + { + "rpi.ccm": + { + "ccms": [ + { + "ct": 2360, + "ccm": + [ + 1.66078, -0.23588, -0.42491, + -0.47456, 1.82763, -0.35307, + -0.00545, -1.44729, 2.45273 + ] + }, + { + "ct": 2870, + "ccm": + [ + 1.78373, -0.55344, -0.23029, + -0.39951, 1.69701, -0.29751, + 0.01986, -1.06525, 2.04539 + ] + }, + { + "ct": 2970, + "ccm": + [ + 1.73511, -0.56973, -0.16537, + -0.36338, 1.69878, -0.33539, + -0.02354, -0.76813, 1.79168 + ] + }, + { + "ct": 3000, + "ccm": + [ + 2.06374, -0.92218, -0.14156, + -0.41721, 1.69289, -0.27568, + -0.00554, -0.92741, 1.93295 + ] + }, + { + "ct": 3700, + "ccm": + [ + 2.13792, -1.08136, -0.05655, + -0.34739, 1.58989, -0.24249, + -0.00349, -0.76789, 1.77138 + ] + }, + { + "ct": 3870, + "ccm": + [ + 1.83834, -0.70528, -0.13307, + -0.30499, 1.60523, -0.30024, + -0.05701, -0.58313, 1.64014 + ] + }, + { + "ct": 4000, + "ccm": + [ + 2.15741, -1.10295, -0.05447, + -0.34631, 1.61158, -0.26528, + -0.02723, -0.70288, 1.73011 + ] + }, + { + "ct": 4400, + "ccm": + [ + 2.05729, -0.95007, -0.10723, + -0.41712, 1.78606, -0.36894, + -0.11899, -0.55727, 1.67626 + ] + }, + { + "ct": 4715, + "ccm": + [ + 1.90255, -0.77478, -0.12777, + -0.31338, 1.88197, -0.56858, + -0.06001, -0.61785, 1.67786 + ] + }, + { + "ct": 5920, + "ccm": + [ + 1.98691, -0.84671, -0.14019, + -0.26581, 1.70615, -0.44035, + -0.09532, -0.47332, 1.56864 + ] + }, + { + "ct": 9050, + "ccm": + [ + 2.09255, -0.76541, -0.32714, + -0.28973, 2.27462, -0.98489, + -0.17299, -0.61275, 1.78574 + ] + } + ] + } + }, + { + "rpi.sharpen": { } + } + ] +} \ No newline at end of file diff --git a/src/ipa/rpi/vc4/data/imx477_scientific.json b/src/ipa/rpi/vc4/data/imx477_scientific.json new file mode 100644 index 00000000..26c692fd --- /dev/null +++ b/src/ipa/rpi/vc4/data/imx477_scientific.json @@ -0,0 +1,479 @@ +{ + "version": 2.0, + "target": "bcm2835", + "algorithms": [ + { + "rpi.black_level": + { + "black_level": 4096 + } + }, + { + "rpi.dpc": { } + }, + { + "rpi.lux": + { + "reference_shutter_speed": 27242, + "reference_gain": 1.0, + "reference_aperture": 1.0, + "reference_lux": 830, + "reference_Y": 17755 + } + }, + { + "rpi.noise": + { + "reference_constant": 0, + "reference_slope": 2.767 + } + }, + { + "rpi.geq": + { + "offset": 204, + "slope": 0.01078 + } + }, + { + "rpi.sdn": { } + }, + { + "rpi.awb": + { + "priors": [ + { + "lux": 0, + "prior": + [ + 2000, 1.0, + 3000, 0.0, + 13000, 0.0 + ] + }, + { + "lux": 800, + "prior": + [ + 2000, 0.0, + 6000, 2.0, + 13000, 2.0 + ] + }, + { + "lux": 1500, + "prior": + [ + 2000, 0.0, + 4000, 1.0, + 6000, 6.0, + 6500, 7.0, + 7000, 1.0, + 13000, 1.0 + ] + } + ], + "modes": + { + "auto": + { + "lo": 2500, + "hi": 8000 + }, + "incandescent": + { + "lo": 2500, + "hi": 3000 + }, + "tungsten": + { + "lo": 3000, + "hi": 3500 + }, + "fluorescent": + { + "lo": 4000, + "hi": 4700 + }, + "indoor": + { + "lo": 3000, + "hi": 5000 + }, + "daylight": + { + "lo": 5500, + "hi": 6500 + }, + "cloudy": + { + "lo": 7000, + "hi": 8600 + } + }, + "bayes": 1, + "ct_curve": + [ + 2000.0, 0.6331025775790707, 0.27424225990946915, + 2200.0, 0.5696117366212947, 0.3116091368689487, + 2400.0, 0.5204264653110015, 0.34892179554105873, + 2600.0, 0.48148675531667223, 0.38565229719076793, + 2800.0, 0.450085403501908, 0.42145684622485047, + 3000.0, 0.42436130159169017, 0.45611835670028816, + 3200.0, 0.40300023695527337, 0.48950766215198593, + 3400.0, 0.3850520052612984, 0.5215567075837261, + 3600.0, 0.36981508088230314, 0.5522397906415475, + 4100.0, 0.333468007836758, 0.5909770465167908, + 4600.0, 0.31196097364221376, 0.6515706327327178, + 5100.0, 0.2961860409294588, 0.7068178946570284, + 5600.0, 0.2842607232745885, 0.7564837749584288, + 6100.0, 0.2750265787051251, 0.8006183524920533, + 6600.0, 0.2677057225584924, 0.8398879225373039, + 7100.0, 0.2617955199757274, 0.8746456080032436, + 7600.0, 0.25693714288250125, 0.905569559506562, + 8100.0, 0.25287531441063316, 0.9331696750390895, + 8600.0, 0.24946601483331993, 0.9576820904825795 + ], + "sensitivity_r": 1.05, + "sensitivity_b": 1.05, + "transverse_pos": 0.0238, + "transverse_neg": 0.04429, + "coarse_step": 0.1 + } + }, + { + "rpi.agc": + { + "metering_modes": + { + "centre-weighted": + { + "weights": [ 3, 3, 3, 2, 2, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0 ] + }, + "spot": + { + "weights": [ 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ] + }, + "matrix": + { + "weights": [ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ] + } + }, + "exposure_modes": + { + "normal": + { + "shutter": [ 100, 10000, 30000, 60000, 66666 ], + "gain": [ 1.0, 2.0, 4.0, 6.0, 8.0 ] + }, + "short": + { + "shutter": [ 100, 5000, 10000, 20000, 33333 ], + "gain": [ 1.0, 2.0, 4.0, 6.0, 8.0 ] + }, + "long": + { + "shutter": [ 100, 10000, 30000, 60000, 120000 ], + "gain": [ 1.0, 2.0, 4.0, 6.0, 12.0 ] + } + }, + "constraint_modes": + { + "normal": [ + { + "bound": "LOWER", + "q_lo": 0.98, + "q_hi": 1.0, + "y_target": + [ + 0, 0.3, + 1000, 0.3 + ] + } + ], + "highlight": [ + { + "bound": "LOWER", + "q_lo": 0.98, + "q_hi": 1.0, + "y_target": + [ + 0, 0.3, + 1000, 0.3 + ] + }, + { + "bound": "UPPER", + "q_lo": 0.98, + "q_hi": 1.0, + "y_target": + [ + 0, 0.8, + 1000, 0.8 + ] + } + ], + "shadows": [ + { + "bound": "LOWER", + "q_lo": 0.0, + "q_hi": 0.5, + "y_target": + [ + 0, 0.17, + 1000, 0.17 + ] + } + ] + }, + "y_target": + [ + 0, 0.16, + 1000, 0.165, + 10000, 0.17 + ] + } + }, + { + "rpi.contrast": + { + "ce_enable": 0, + "gamma_curve": + [ + 0, 0, + 512, 2304, + 1024, 4608, + 1536, 6573, + 2048, 8401, + 2560, 9992, + 3072, 11418, + 3584, 12719, + 4096, 13922, + 4608, 15045, + 5120, 16103, + 5632, 17104, + 6144, 18056, + 6656, 18967, + 7168, 19839, + 7680, 20679, + 8192, 21488, + 9216, 23028, + 10240, 24477, + 11264, 25849, + 12288, 27154, + 13312, 28401, + 14336, 29597, + 15360, 30747, + 16384, 31856, + 17408, 32928, + 18432, 33966, + 19456, 34973, + 20480, 35952, + 22528, 37832, + 24576, 39621, + 26624, 41330, + 28672, 42969, + 30720, 44545, + 32768, 46065, + 34816, 47534, + 36864, 48956, + 38912, 50336, + 40960, 51677, + 43008, 52982, + 45056, 54253, + 47104, 55493, + 49152, 56704, + 51200, 57888, + 53248, 59046, + 55296, 60181, + 57344, 61292, + 59392, 62382, + 61440, 63452, + 63488, 64503, + 65535, 65535 + ] + } + }, + { + "rpi.ccm": + { + "ccms": [ + { + "ct": 2000, + "ccm": + [ + 1.5813882365848004, -0.35293683714581114, -0.27378771561617715, + -0.4347297185453639, 1.5792631087746074, -0.12102601986382337, + 0.2322290578987574, -1.4382672640468128, 2.1386425781770755 + ] + }, + { + "ct": 2200, + "ccm": + [ + 1.6322048484088305, -0.45932286857238486, -0.21373542690252198, + -0.3970719209901105, 1.5877868651467202, -0.17249380832122455, + 0.20753774825903412, -1.2660673594740142, 2.005654261091916 + ] + }, + { + "ct": 2400, + "ccm": + [ + 1.6766610071470398, -0.5447101051688111, -0.16838641107407676, + -0.3659845183388154, 1.592223692670396, -0.2127091997471162, + 0.1833964516767549, -1.1339155942419321, 1.9089342978542396 + ] + }, + { + "ct": 2600, + "ccm": + [ + 1.7161984340622154, -0.6152585785678794, -0.1331100845092582, + -0.33972082628066275, 1.5944888273736966, -0.2453979465898787, + 0.1615577497676328, -1.0298684958833109, 1.8357854177422053 + ] + }, + { + "ct": 2800, + "ccm": + [ + 1.7519307259815728, -0.6748682080165339, -0.10515169074540848, + -0.3171703484479931, 1.5955820297498486, -0.2727395854813966, + 0.14230870739974305, -0.9460976023551511, 1.778709391659538 + ] + }, + { + "ct": 3000, + "ccm": + [ + 1.7846716625128374, -0.7261240476375332, -0.08274697420358428, + -0.2975654035173307, 1.5960425637021738, -0.2961043416505157, + 0.12546426281675097, -0.8773434727076518, 1.7330356805246685 + ] + }, + { + "ct": 3200, + "ccm": + [ + 1.8150085872943436, -0.7708109672515514, -0.06469468211419174, + -0.2803468940646277, 1.596168842967451, -0.3164044170681625, + 0.11071494533513807, -0.8199772290209191, 1.69572135046367 + ] + }, + { + "ct": 3400, + "ccm": + [ + 1.8433668304932087, -0.8102060605062592, -0.05013485852801454, + -0.2650934036324084, 1.5961288492969294, -0.33427554893845535, + 0.0977478941863518, -0.7714303112098978, 1.6647070820146963 + ] + }, + { + "ct": 3600, + "ccm": + [ + 1.8700575831917468, -0.8452518300291346, -0.03842644337477299, + -0.2514794528347016, 1.5960178299141876, -0.3501774949366156, + 0.08628520830733245, -0.729841503339915, 1.638553343939267 + ] + }, + { + "ct": 4100, + "ccm": + [ + 1.8988700903560716, -0.8911278803351247, -0.018848644425650693, + -0.21487101487384094, 1.599236541382614, -0.39405450457918206, + 0.08251488056482173, -0.7178919368326191, 1.6267009056502704 + ] + }, + { + "ct": 4600, + "ccm": + [ + 1.960355191764125, -0.9624344812121991, -0.0017122408632169205, + -0.19444620905212898, 1.5978493736948447, -0.416727638296156, + 0.06310261513271084, -0.6483790952487849, 1.5834605477213093 + ] + }, + { + "ct": 5100, + "ccm": + [ + 2.014680536961399, -1.0195930302148566, 0.007728256612638915, + -0.17751999660735496, 1.5977081555831, -0.4366085498741474, + 0.04741267583041334, -0.5950327902073489, 1.5512919847321853 + ] + }, + { + "ct": 5600, + "ccm": + [ + 2.062652337917251, -1.0658386679125478, 0.011886354256281267, + -0.16319197721451495, 1.598363237584736, -0.45422061523742235, + 0.03465810928795378, -0.5535454108047286, 1.5269025836946852 + ] + }, + { + "ct": 6100, + "ccm": + [ + 2.104985902038069, -1.103597868736314, 0.012503517136539277, + -0.15090797064906178, 1.5994703078166095, -0.4698414300864995, + 0.02421766063474242, -0.5208922818196823, 1.5081270847783788 + ] + }, + { + "ct": 6600, + "ccm": + [ + 2.1424988751299714, -1.134760232367728, 0.010730356010435522, + -0.14021846798466234, 1.600822462230719, -0.48379204794526487, + 0.015521315410496622, -0.49463630325832275, 1.4933313534840327 + ] + }, + { + "ct": 7100, + "ccm": + [ + 2.1758034100130925, -1.1607558481037359, 0.007452724895469076, + -0.13085694672641826, 1.6022648614493245, -0.4962330524084075, + 0.008226943206113427, -0.4733077192319791, 1.4815336120437468 + ] + }, + { + "ct": 7600, + "ccm": + [ + 2.205529206931895, -1.1826662383072108, 0.0032019529917605167, + -0.122572009780486, 1.6037258133595753, -0.5073973734282445, + 0.0020132587619863425, -0.4556590236414181, 1.471939788496745 + ] + }, + { + "ct": 8100, + "ccm": + [ + 2.232224969223067, -1.2013672897252885, -0.0016234598095482985, + -0.11518026734442414, 1.6051544769439803, -0.5174558699422255, + -0.0033378143542219835, -0.4408590373867774, 1.4640252230667452 + ] + }, + { + "ct": 8600, + "ccm": + [ + 2.256082295891265, -1.2173210549996634, -0.0067231350481711675, + -0.10860272839843167, 1.6065150139140594, -0.5264728573611493, + -0.007952618707984149, -0.4284003574050791, 1.4574646927117558 + ] + } + ] + } + }, + { + "rpi.sharpen": { } + } + ] +} \ No newline at end of file diff --git a/src/ipa/rpi/vc4/data/imx477_v1.json b/src/ipa/rpi/vc4/data/imx477_v1.json new file mode 100644 index 00000000..d6402009 --- /dev/null +++ b/src/ipa/rpi/vc4/data/imx477_v1.json @@ -0,0 +1,516 @@ +{ + "version": 2.0, + "target": "bcm2835", + "algorithms": [ + { + "rpi.black_level": + { + "black_level": 4096 + } + }, + { + "rpi.dpc": { } + }, + { + "rpi.lux": + { + "reference_shutter_speed": 27242, + "reference_gain": 1.0, + "reference_aperture": 1.0, + "reference_lux": 830, + "reference_Y": 17755 + } + }, + { + "rpi.noise": + { + "reference_constant": 0, + "reference_slope": 2.767 + } + }, + { + "rpi.geq": + { + "offset": 204, + "slope": 0.01078 + } + }, + { + "rpi.sdn": { } + }, + { + "rpi.awb": + { + "priors": [ + { + "lux": 0, + "prior": + [ + 2000, 1.0, + 3000, 0.0, + 13000, 0.0 + ] + }, + { + "lux": 800, + "prior": + [ + 2000, 0.0, + 6000, 2.0, + 13000, 2.0 + ] + }, + { + "lux": 1500, + "prior": + [ + 2000, 0.0, + 4000, 1.0, + 6000, 6.0, + 6500, 7.0, + 7000, 1.0, + 13000, 1.0 + ] + } + ], + "modes": + { + "auto": + { + "lo": 2500, + "hi": 8000 + }, + "incandescent": + { + "lo": 2500, + "hi": 3000 + }, + "tungsten": + { + "lo": 3000, + "hi": 3500 + }, + "fluorescent": + { + "lo": 4000, + "hi": 4700 + }, + "indoor": + { + "lo": 3000, + "hi": 5000 + }, + "daylight": + { + "lo": 5500, + "hi": 6500 + }, + "cloudy": + { + "lo": 7000, + "hi": 8600 + } + }, + "bayes": 1, + "ct_curve": + [ + 2360.0, 0.6009, 0.3093, + 2870.0, 0.5047, 0.3936, + 2970.0, 0.4782, 0.4221, + 3700.0, 0.4212, 0.4923, + 3870.0, 0.4037, 0.5166, + 4000.0, 0.3965, 0.5271, + 4400.0, 0.3703, 0.5666, + 4715.0, 0.3411, 0.6147, + 5920.0, 0.3108, 0.6687, + 9050.0, 0.2524, 0.7856 + ], + "sensitivity_r": 1.05, + "sensitivity_b": 1.05, + "transverse_pos": 0.0238, + "transverse_neg": 0.04429 + } + }, + { + "rpi.agc": + { + "metering_modes": + { + "centre-weighted": + { + "weights": [ 3, 3, 3, 2, 2, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0 ] + }, + "spot": + { + "weights": [ 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ] + }, + "matrix": + { + "weights": [ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ] + } + }, + "exposure_modes": + { + "normal": + { + "shutter": [ 100, 10000, 30000, 60000, 66666 ], + "gain": [ 1.0, 2.0, 4.0, 6.0, 8.0 ] + }, + "short": + { + "shutter": [ 100, 5000, 10000, 20000, 33333 ], + "gain": [ 1.0, 2.0, 4.0, 6.0, 8.0 ] + }, + "long": + { + "shutter": [ 100, 10000, 30000, 60000, 120000 ], + "gain": [ 1.0, 2.0, 4.0, 6.0, 12.0 ] + } + }, + "constraint_modes": + { + "normal": [ + { + "bound": "LOWER", + "q_lo": 0.98, + "q_hi": 1.0, + "y_target": + [ + 0, 0.3, + 1000, 0.3 + ] + } + ], + "highlight": [ + { + "bound": "LOWER", + "q_lo": 0.98, + "q_hi": 1.0, + "y_target": + [ + 0, 0.3, + 1000, 0.3 + ] + }, + { + "bound": "UPPER", + "q_lo": 0.98, + "q_hi": 1.0, + "y_target": + [ + 0, 0.8, + 1000, 0.8 + ] + } + ], + "shadows": [ + { + "bound": "LOWER", + "q_lo": 0.0, + "q_hi": 0.5, + "y_target": + [ + 0, 0.17, + 1000, 0.17 + ] + } + ] + }, + "y_target": + [ + 0, 0.16, + 1000, 0.165, + 10000, 0.17 + ] + } + }, + { + "rpi.alsc": + { + "omega": 1.3, + "n_iter": 100, + "luminance_strength": 0.5, + "calibrations_Cr": [ + { + "ct": 2960, + "table": + [ + 2.088, 2.086, 2.082, 2.081, 2.077, 2.071, 2.068, 2.068, 2.072, 2.073, 2.075, 2.078, 2.084, 2.092, 2.095, 2.098, + 2.086, 2.084, 2.079, 2.078, 2.075, 2.068, 2.064, 2.063, 2.068, 2.071, 2.072, 2.075, 2.081, 2.089, 2.092, 2.094, + 2.083, 2.081, 2.077, 2.072, 2.069, 2.062, 2.059, 2.059, 2.063, 2.067, 2.069, 2.072, 2.079, 2.088, 2.089, 2.089, + 2.081, 2.077, 2.072, 2.068, 2.065, 2.058, 2.055, 2.054, 2.057, 2.062, 2.066, 2.069, 2.077, 2.084, 2.086, 2.086, + 2.078, 2.075, 2.069, 2.065, 2.061, 2.055, 2.052, 2.049, 2.051, 2.056, 2.062, 2.065, 2.072, 2.079, 2.081, 2.079, + 2.079, 2.075, 2.069, 2.064, 2.061, 2.053, 2.049, 2.046, 2.049, 2.051, 2.057, 2.062, 2.069, 2.075, 2.077, 2.075, + 2.082, 2.079, 2.072, 2.065, 2.061, 2.054, 2.049, 2.047, 2.049, 2.051, 2.056, 2.061, 2.066, 2.073, 2.073, 2.069, + 2.086, 2.082, 2.075, 2.068, 2.062, 2.054, 2.051, 2.049, 2.051, 2.052, 2.056, 2.061, 2.066, 2.073, 2.073, 2.072, + 2.088, 2.086, 2.079, 2.074, 2.066, 2.057, 2.051, 2.051, 2.054, 2.055, 2.056, 2.061, 2.067, 2.072, 2.073, 2.072, + 2.091, 2.087, 2.079, 2.075, 2.068, 2.057, 2.052, 2.052, 2.056, 2.055, 2.055, 2.059, 2.066, 2.072, 2.072, 2.072, + 2.093, 2.088, 2.081, 2.077, 2.069, 2.059, 2.054, 2.054, 2.057, 2.056, 2.056, 2.058, 2.066, 2.072, 2.073, 2.073, + 2.095, 2.091, 2.084, 2.078, 2.075, 2.067, 2.057, 2.057, 2.059, 2.059, 2.058, 2.059, 2.068, 2.073, 2.075, 2.078 + ] + }, + { + "ct": 4850, + "table": + [ + 2.973, 2.968, 2.956, 2.943, 2.941, 2.932, 2.923, 2.921, 2.924, 2.929, 2.931, 2.939, 2.953, 2.965, 2.966, 2.976, + 2.969, 2.962, 2.951, 2.941, 2.934, 2.928, 2.919, 2.918, 2.919, 2.923, 2.927, 2.933, 2.945, 2.957, 2.962, 2.962, + 2.964, 2.956, 2.944, 2.932, 2.929, 2.924, 2.915, 2.914, 2.915, 2.919, 2.924, 2.928, 2.941, 2.952, 2.958, 2.959, + 2.957, 2.951, 2.939, 2.928, 2.924, 2.919, 2.913, 2.911, 2.911, 2.915, 2.919, 2.925, 2.936, 2.947, 2.952, 2.953, + 2.954, 2.947, 2.935, 2.924, 2.919, 2.915, 2.908, 2.906, 2.906, 2.907, 2.914, 2.921, 2.932, 2.941, 2.943, 2.942, + 2.953, 2.946, 2.932, 2.921, 2.916, 2.911, 2.904, 2.902, 2.901, 2.904, 2.909, 2.919, 2.926, 2.937, 2.939, 2.939, + 2.953, 2.947, 2.932, 2.918, 2.915, 2.909, 2.903, 2.901, 2.901, 2.906, 2.911, 2.918, 2.924, 2.936, 2.936, 2.932, + 2.956, 2.948, 2.934, 2.919, 2.916, 2.908, 2.903, 2.901, 2.902, 2.907, 2.909, 2.917, 2.926, 2.936, 2.939, 2.939, + 2.957, 2.951, 2.936, 2.923, 2.917, 2.907, 2.904, 2.901, 2.902, 2.908, 2.911, 2.919, 2.929, 2.939, 2.942, 2.942, + 2.961, 2.951, 2.936, 2.922, 2.918, 2.906, 2.904, 2.901, 2.901, 2.907, 2.911, 2.921, 2.931, 2.941, 2.942, 2.944, + 2.964, 2.954, 2.936, 2.924, 2.918, 2.909, 2.905, 2.905, 2.905, 2.907, 2.912, 2.923, 2.933, 2.942, 2.944, 2.944, + 2.964, 2.958, 2.943, 2.927, 2.921, 2.914, 2.909, 2.907, 2.907, 2.912, 2.916, 2.928, 2.936, 2.944, 2.947, 2.952 + ] + }, + { + "ct": 5930, + "table": + [ + 3.312, 3.308, 3.301, 3.294, 3.288, 3.277, 3.268, 3.261, 3.259, 3.261, 3.267, 3.273, 3.285, 3.301, 3.303, 3.312, + 3.308, 3.304, 3.294, 3.291, 3.283, 3.271, 3.263, 3.259, 3.257, 3.258, 3.261, 3.268, 3.278, 3.293, 3.299, 3.299, + 3.302, 3.296, 3.288, 3.282, 3.276, 3.267, 3.259, 3.254, 3.252, 3.253, 3.256, 3.261, 3.273, 3.289, 3.292, 3.292, + 3.296, 3.289, 3.282, 3.276, 3.269, 3.263, 3.256, 3.251, 3.248, 3.249, 3.251, 3.257, 3.268, 3.279, 3.284, 3.284, + 3.292, 3.285, 3.279, 3.271, 3.264, 3.257, 3.249, 3.243, 3.241, 3.241, 3.246, 3.252, 3.261, 3.274, 3.275, 3.273, + 3.291, 3.285, 3.276, 3.268, 3.259, 3.251, 3.242, 3.239, 3.236, 3.238, 3.244, 3.248, 3.258, 3.268, 3.269, 3.265, + 3.294, 3.288, 3.275, 3.266, 3.257, 3.248, 3.239, 3.238, 3.237, 3.238, 3.243, 3.246, 3.255, 3.264, 3.264, 3.257, + 3.297, 3.293, 3.279, 3.268, 3.258, 3.249, 3.238, 3.237, 3.239, 3.239, 3.243, 3.245, 3.255, 3.264, 3.264, 3.263, + 3.301, 3.295, 3.281, 3.271, 3.259, 3.248, 3.237, 3.237, 3.239, 3.241, 3.243, 3.246, 3.257, 3.265, 3.266, 3.264, + 3.306, 3.295, 3.279, 3.271, 3.261, 3.247, 3.235, 3.234, 3.239, 3.239, 3.243, 3.247, 3.258, 3.265, 3.265, 3.264, + 3.308, 3.297, 3.279, 3.272, 3.261, 3.249, 3.239, 3.239, 3.241, 3.243, 3.245, 3.248, 3.261, 3.265, 3.266, 3.265, + 3.309, 3.301, 3.286, 3.276, 3.267, 3.256, 3.246, 3.242, 3.244, 3.244, 3.249, 3.253, 3.263, 3.267, 3.271, 3.274 + ] + } + ], + "calibrations_Cb": [ + { + "ct": 2960, + "table": + [ + 2.133, 2.134, 2.139, 2.143, 2.148, 2.155, 2.158, 2.158, 2.158, 2.161, 2.161, 2.162, 2.159, 2.156, 2.152, 2.151, + 2.132, 2.133, 2.135, 2.142, 2.147, 2.153, 2.158, 2.158, 2.158, 2.158, 2.159, 2.159, 2.157, 2.154, 2.151, 2.148, + 2.133, 2.133, 2.135, 2.142, 2.149, 2.154, 2.158, 2.158, 2.157, 2.156, 2.158, 2.157, 2.155, 2.153, 2.148, 2.146, + 2.133, 2.133, 2.138, 2.145, 2.149, 2.154, 2.158, 2.159, 2.158, 2.155, 2.157, 2.156, 2.153, 2.149, 2.146, 2.144, + 2.133, 2.134, 2.139, 2.146, 2.149, 2.154, 2.158, 2.159, 2.159, 2.156, 2.154, 2.154, 2.149, 2.145, 2.143, 2.139, + 2.135, 2.135, 2.139, 2.146, 2.151, 2.155, 2.158, 2.159, 2.158, 2.156, 2.153, 2.151, 2.146, 2.143, 2.139, 2.136, + 2.135, 2.135, 2.138, 2.145, 2.151, 2.154, 2.157, 2.158, 2.157, 2.156, 2.153, 2.151, 2.147, 2.143, 2.141, 2.137, + 2.135, 2.134, 2.135, 2.141, 2.149, 2.154, 2.157, 2.157, 2.157, 2.157, 2.157, 2.153, 2.149, 2.146, 2.142, 2.139, + 2.132, 2.133, 2.135, 2.139, 2.148, 2.153, 2.158, 2.159, 2.159, 2.161, 2.161, 2.157, 2.154, 2.149, 2.144, 2.141, + 2.132, 2.133, 2.135, 2.141, 2.149, 2.155, 2.161, 2.161, 2.162, 2.162, 2.163, 2.159, 2.154, 2.149, 2.144, 2.138, + 2.136, 2.136, 2.137, 2.143, 2.149, 2.156, 2.162, 2.163, 2.162, 2.163, 2.164, 2.161, 2.157, 2.152, 2.146, 2.138, + 2.137, 2.137, 2.141, 2.147, 2.152, 2.157, 2.162, 2.162, 2.159, 2.161, 2.162, 2.162, 2.157, 2.152, 2.148, 2.148 + ] + }, + { + "ct": 4850, + "table": + [ + 1.463, 1.464, 1.471, 1.478, 1.479, 1.483, 1.484, 1.486, 1.486, 1.484, 1.483, 1.481, 1.478, 1.475, 1.471, 1.468, + 1.463, 1.463, 1.468, 1.476, 1.479, 1.482, 1.484, 1.487, 1.486, 1.484, 1.483, 1.482, 1.478, 1.473, 1.469, 1.468, + 1.463, 1.464, 1.468, 1.476, 1.479, 1.483, 1.484, 1.486, 1.486, 1.485, 1.484, 1.482, 1.477, 1.473, 1.469, 1.468, + 1.463, 1.464, 1.469, 1.477, 1.481, 1.483, 1.485, 1.487, 1.487, 1.485, 1.485, 1.482, 1.478, 1.474, 1.469, 1.468, + 1.465, 1.465, 1.471, 1.478, 1.481, 1.484, 1.486, 1.488, 1.488, 1.487, 1.485, 1.482, 1.477, 1.472, 1.468, 1.467, + 1.465, 1.466, 1.472, 1.479, 1.482, 1.485, 1.486, 1.488, 1.488, 1.486, 1.484, 1.479, 1.475, 1.472, 1.468, 1.466, + 1.466, 1.466, 1.472, 1.478, 1.482, 1.484, 1.485, 1.488, 1.487, 1.485, 1.483, 1.479, 1.475, 1.472, 1.469, 1.468, + 1.465, 1.466, 1.469, 1.476, 1.481, 1.485, 1.485, 1.486, 1.486, 1.485, 1.483, 1.479, 1.477, 1.474, 1.471, 1.469, + 1.464, 1.465, 1.469, 1.476, 1.481, 1.484, 1.485, 1.487, 1.487, 1.486, 1.485, 1.481, 1.478, 1.475, 1.471, 1.469, + 1.463, 1.464, 1.469, 1.477, 1.481, 1.485, 1.485, 1.488, 1.488, 1.487, 1.486, 1.481, 1.478, 1.475, 1.471, 1.468, + 1.464, 1.465, 1.471, 1.478, 1.482, 1.486, 1.486, 1.488, 1.488, 1.487, 1.486, 1.481, 1.478, 1.475, 1.472, 1.468, + 1.465, 1.466, 1.472, 1.481, 1.483, 1.487, 1.487, 1.488, 1.488, 1.486, 1.485, 1.481, 1.479, 1.476, 1.473, 1.472 + ] + }, + { + "ct": 5930, + "table": + [ + 1.443, 1.444, 1.448, 1.453, 1.459, 1.463, 1.465, 1.467, 1.469, 1.469, 1.467, 1.466, 1.462, 1.457, 1.454, 1.451, + 1.443, 1.444, 1.445, 1.451, 1.459, 1.463, 1.465, 1.467, 1.469, 1.469, 1.467, 1.465, 1.461, 1.456, 1.452, 1.451, + 1.444, 1.444, 1.445, 1.451, 1.459, 1.463, 1.466, 1.468, 1.469, 1.469, 1.467, 1.465, 1.461, 1.456, 1.452, 1.449, + 1.444, 1.444, 1.447, 1.452, 1.459, 1.464, 1.467, 1.469, 1.471, 1.469, 1.467, 1.466, 1.461, 1.456, 1.452, 1.449, + 1.444, 1.445, 1.448, 1.452, 1.459, 1.465, 1.469, 1.471, 1.471, 1.471, 1.468, 1.465, 1.461, 1.455, 1.451, 1.449, + 1.445, 1.446, 1.449, 1.453, 1.461, 1.466, 1.469, 1.471, 1.472, 1.469, 1.467, 1.465, 1.459, 1.455, 1.451, 1.447, + 1.446, 1.446, 1.449, 1.453, 1.461, 1.466, 1.469, 1.469, 1.469, 1.469, 1.467, 1.465, 1.459, 1.455, 1.452, 1.449, + 1.446, 1.446, 1.447, 1.451, 1.459, 1.466, 1.469, 1.469, 1.469, 1.469, 1.467, 1.465, 1.461, 1.457, 1.454, 1.451, + 1.444, 1.444, 1.447, 1.451, 1.459, 1.466, 1.469, 1.469, 1.471, 1.471, 1.468, 1.466, 1.462, 1.458, 1.454, 1.452, + 1.444, 1.444, 1.448, 1.453, 1.459, 1.466, 1.469, 1.471, 1.472, 1.472, 1.468, 1.466, 1.462, 1.458, 1.454, 1.449, + 1.446, 1.447, 1.449, 1.454, 1.461, 1.466, 1.471, 1.471, 1.471, 1.471, 1.468, 1.466, 1.462, 1.459, 1.455, 1.449, + 1.447, 1.447, 1.452, 1.457, 1.462, 1.468, 1.472, 1.472, 1.471, 1.471, 1.468, 1.466, 1.462, 1.459, 1.456, 1.455 + ] + } + ], + "luminance_lut": + [ + 1.548, 1.499, 1.387, 1.289, 1.223, 1.183, 1.164, 1.154, 1.153, 1.169, 1.211, 1.265, 1.345, 1.448, 1.581, 1.619, + 1.513, 1.412, 1.307, 1.228, 1.169, 1.129, 1.105, 1.098, 1.103, 1.127, 1.157, 1.209, 1.272, 1.361, 1.481, 1.583, + 1.449, 1.365, 1.257, 1.175, 1.124, 1.085, 1.062, 1.054, 1.059, 1.079, 1.113, 1.151, 1.211, 1.293, 1.407, 1.488, + 1.424, 1.324, 1.222, 1.139, 1.089, 1.056, 1.034, 1.031, 1.034, 1.049, 1.075, 1.115, 1.164, 1.241, 1.351, 1.446, + 1.412, 1.297, 1.203, 1.119, 1.069, 1.039, 1.021, 1.016, 1.022, 1.032, 1.052, 1.086, 1.135, 1.212, 1.321, 1.439, + 1.406, 1.287, 1.195, 1.115, 1.059, 1.028, 1.014, 1.012, 1.015, 1.026, 1.041, 1.074, 1.125, 1.201, 1.302, 1.425, + 1.406, 1.294, 1.205, 1.126, 1.062, 1.031, 1.013, 1.009, 1.011, 1.019, 1.042, 1.079, 1.129, 1.203, 1.302, 1.435, + 1.415, 1.318, 1.229, 1.146, 1.076, 1.039, 1.019, 1.014, 1.017, 1.031, 1.053, 1.093, 1.144, 1.219, 1.314, 1.436, + 1.435, 1.348, 1.246, 1.164, 1.094, 1.059, 1.036, 1.032, 1.037, 1.049, 1.072, 1.114, 1.167, 1.257, 1.343, 1.462, + 1.471, 1.385, 1.278, 1.189, 1.124, 1.084, 1.064, 1.061, 1.069, 1.078, 1.101, 1.146, 1.207, 1.298, 1.415, 1.496, + 1.522, 1.436, 1.323, 1.228, 1.169, 1.118, 1.101, 1.094, 1.099, 1.113, 1.146, 1.194, 1.265, 1.353, 1.474, 1.571, + 1.578, 1.506, 1.378, 1.281, 1.211, 1.156, 1.135, 1.134, 1.139, 1.158, 1.194, 1.251, 1.327, 1.427, 1.559, 1.611 + ], + "sigma": 0.00121, + "sigma_Cb": 0.00115 + } + }, + { + "rpi.contrast": + { + "ce_enable": 1, + "gamma_curve": + [ + 0, 0, + 1024, 5040, + 2048, 9338, + 3072, 12356, + 4096, 15312, + 5120, 18051, + 6144, 20790, + 7168, 23193, + 8192, 25744, + 9216, 27942, + 10240, 30035, + 11264, 32005, + 12288, 33975, + 13312, 35815, + 14336, 37600, + 15360, 39168, + 16384, 40642, + 18432, 43379, + 20480, 45749, + 22528, 47753, + 24576, 49621, + 26624, 51253, + 28672, 52698, + 30720, 53796, + 32768, 54876, + 36864, 57012, + 40960, 58656, + 45056, 59954, + 49152, 61183, + 53248, 62355, + 57344, 63419, + 61440, 64476, + 65535, 65535 + ] + } + }, + { + "rpi.ccm": + { + "ccms": [ + { + "ct": 2360, + "ccm": + [ + 1.66078, -0.23588, -0.42491, + -0.47456, 1.82763, -0.35307, + -0.00545, -1.44729, 2.45273 + ] + }, + { + "ct": 2870, + "ccm": + [ + 1.78373, -0.55344, -0.23029, + -0.39951, 1.69701, -0.29751, + 0.01986, -1.06525, 2.04539 + ] + }, + { + "ct": 2970, + "ccm": + [ + 1.73511, -0.56973, -0.16537, + -0.36338, 1.69878, -0.33539, + -0.02354, -0.76813, 1.79168 + ] + }, + { + "ct": 3000, + "ccm": + [ + 2.06374, -0.92218, -0.14156, + -0.41721, 1.69289, -0.27568, + -0.00554, -0.92741, 1.93295 + ] + }, + { + "ct": 3700, + "ccm": + [ + 2.13792, -1.08136, -0.05655, + -0.34739, 1.58989, -0.24249, + -0.00349, -0.76789, 1.77138 + ] + }, + { + "ct": 3870, + "ccm": + [ + 1.83834, -0.70528, -0.13307, + -0.30499, 1.60523, -0.30024, + -0.05701, -0.58313, 1.64014 + ] + }, + { + "ct": 4000, + "ccm": + [ + 2.15741, -1.10295, -0.05447, + -0.34631, 1.61158, -0.26528, + -0.02723, -0.70288, 1.73011 + ] + }, + { + "ct": 4400, + "ccm": + [ + 2.05729, -0.95007, -0.10723, + -0.41712, 1.78606, -0.36894, + -0.11899, -0.55727, 1.67626 + ] + }, + { + "ct": 4715, + "ccm": + [ + 1.90255, -0.77478, -0.12777, + -0.31338, 1.88197, -0.56858, + -0.06001, -0.61785, 1.67786 + ] + }, + { + "ct": 5920, + "ccm": + [ + 1.98691, -0.84671, -0.14019, + -0.26581, 1.70615, -0.44035, + -0.09532, -0.47332, 1.56864 + ] + }, + { + "ct": 9050, + "ccm": + [ + 2.09255, -0.76541, -0.32714, + -0.28973, 2.27462, -0.98489, + -0.17299, -0.61275, 1.78574 + ] + } + ] + } + }, + { + "rpi.sharpen": { } + } + ] +} \ No newline at end of file diff --git a/src/ipa/rpi/vc4/data/imx519.json b/src/ipa/rpi/vc4/data/imx519.json new file mode 100644 index 00000000..8ccfd3a6 --- /dev/null +++ b/src/ipa/rpi/vc4/data/imx519.json @@ -0,0 +1,413 @@ +{ + "version": 2.0, + "target": "bcm2835", + "algorithms": [ + { + "rpi.black_level": + { + "black_level": 4096 + } + }, + { + "rpi.dpc": { } + }, + { + "rpi.lux": + { + "reference_shutter_speed": 13841, + "reference_gain": 2.0, + "reference_aperture": 1.0, + "reference_lux": 900, + "reference_Y": 12064 + } + }, + { + "rpi.noise": + { + "reference_constant": 0, + "reference_slope": 2.776 + } + }, + { + "rpi.geq": + { + "offset": 189, + "slope": 0.01495 + } + }, + { + "rpi.sdn": { } + }, + { + "rpi.awb": + { + "priors": [ + { + "lux": 0, + "prior": + [ + 2000, 1.0, + 3000, 0.0, + 13000, 0.0 + ] + }, + { + "lux": 800, + "prior": + [ + 2000, 0.0, + 6000, 2.0, + 13000, 2.0 + ] + }, + { + "lux": 1500, + "prior": + [ + 2000, 0.0, + 4000, 1.0, + 6000, 6.0, + 6500, 7.0, + 7000, 1.0, + 13000, 1.0 + ] + } + ], + "modes": + { + "auto": + { + "lo": 2500, + "hi": 7900 + }, + "incandescent": + { + "lo": 2500, + "hi": 3000 + }, + "tungsten": + { + "lo": 3000, + "hi": 3500 + }, + "fluorescent": + { + "lo": 4000, + "hi": 4700 + }, + "indoor": + { + "lo": 3000, + "hi": 5000 + }, + "daylight": + { + "lo": 5500, + "hi": 6500 + }, + "cloudy": + { + "lo": 7000, + "hi": 8000 + } + }, + "bayes": 1, + "ct_curve": + [ + 2890.0, 0.7328, 0.3734, + 3550.0, 0.6228, 0.4763, + 4500.0, 0.5208, 0.5825, + 5700.0, 0.4467, 0.6671, + 7900.0, 0.3858, 0.7411 + ], + "sensitivity_r": 1.0, + "sensitivity_b": 1.0, + "transverse_pos": 0.02027, + "transverse_neg": 0.01935 + } + }, + { + "rpi.agc": + { + "metering_modes": + { + "centre-weighted": + { + "weights": [ 3, 3, 3, 2, 2, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0 ] + }, + "spot": + { + "weights": [ 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ] + }, + "matrix": + { + "weights": [ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ] + } + }, + "exposure_modes": + { + "normal": + { + "shutter": [ 100, 10000, 30000, 60000, 120000 ], + "gain": [ 1.0, 2.0, 4.0, 6.0, 8.0 ] + }, + "short": + { + "shutter": [ 100, 5000, 10000, 20000, 120000 ], + "gain": [ 1.0, 2.0, 4.0, 6.0, 8.0 ] + } + }, + "constraint_modes": + { + "normal": [ + { + "bound": "LOWER", + "q_lo": 0.98, + "q_hi": 1.0, + "y_target": + [ + 0, 0.5, + 1000, 0.5 + ] + } + ], + "highlight": [ + { + "bound": "LOWER", + "q_lo": 0.98, + "q_hi": 1.0, + "y_target": + [ + 0, 0.5, + 1000, 0.5 + ] + }, + { + "bound": "UPPER", + "q_lo": 0.98, + "q_hi": 1.0, + "y_target": + [ + 0, 0.8, + 1000, 0.8 + ] + } + ] + }, + "y_target": + [ + 0, 0.16, + 1000, 0.165, + 10000, 0.17 + ] + } + }, + { + "rpi.alsc": + { + "omega": 1.3, + "n_iter": 100, + "luminance_strength": 0.5, + "calibrations_Cr": [ + { + "ct": 3000, + "table": + [ + 1.527, 1.521, 1.508, 1.493, 1.476, 1.455, 1.442, 1.441, 1.441, 1.441, 1.448, 1.467, 1.483, 1.494, 1.503, 1.504, + 1.525, 1.513, 1.496, 1.477, 1.461, 1.434, 1.418, 1.409, 1.409, 1.416, 1.429, 1.449, 1.469, 1.485, 1.495, 1.503, + 1.517, 1.506, 1.485, 1.461, 1.434, 1.412, 1.388, 1.376, 1.376, 1.386, 1.405, 1.429, 1.449, 1.471, 1.488, 1.495, + 1.512, 1.496, 1.471, 1.442, 1.412, 1.388, 1.361, 1.344, 1.344, 1.358, 1.384, 1.405, 1.431, 1.456, 1.479, 1.489, + 1.508, 1.488, 1.458, 1.425, 1.393, 1.361, 1.343, 1.322, 1.321, 1.342, 1.358, 1.385, 1.416, 1.445, 1.471, 1.484, + 1.507, 1.482, 1.453, 1.418, 1.382, 1.349, 1.322, 1.318, 1.318, 1.321, 1.345, 1.373, 1.405, 1.437, 1.465, 1.483, + 1.507, 1.482, 1.453, 1.418, 1.382, 1.349, 1.322, 1.313, 1.313, 1.321, 1.345, 1.373, 1.405, 1.437, 1.465, 1.483, + 1.507, 1.485, 1.455, 1.422, 1.387, 1.355, 1.333, 1.319, 1.321, 1.333, 1.351, 1.381, 1.411, 1.441, 1.467, 1.483, + 1.508, 1.489, 1.463, 1.432, 1.401, 1.372, 1.355, 1.333, 1.333, 1.351, 1.369, 1.393, 1.422, 1.448, 1.471, 1.484, + 1.511, 1.494, 1.472, 1.444, 1.416, 1.398, 1.372, 1.361, 1.361, 1.369, 1.393, 1.411, 1.436, 1.458, 1.477, 1.487, + 1.511, 1.496, 1.478, 1.455, 1.436, 1.416, 1.399, 1.391, 1.391, 1.397, 1.411, 1.429, 1.451, 1.466, 1.479, 1.487, + 1.511, 1.495, 1.478, 1.462, 1.448, 1.432, 1.419, 1.419, 1.419, 1.419, 1.429, 1.445, 1.459, 1.471, 1.482, 1.487 + ] + }, + { + "ct": 6000, + "table": + [ + 2.581, 2.573, 2.558, 2.539, 2.514, 2.487, 2.473, 2.471, 2.471, 2.471, 2.479, 2.499, 2.517, 2.532, 2.543, 2.544, + 2.575, 2.559, 2.539, 2.521, 2.491, 2.458, 2.435, 2.421, 2.421, 2.429, 2.449, 2.477, 2.499, 2.519, 2.534, 2.543, + 2.561, 2.549, 2.521, 2.491, 2.457, 2.423, 2.393, 2.375, 2.375, 2.387, 2.412, 2.444, 2.475, 2.499, 2.519, 2.532, + 2.552, 2.531, 2.498, 2.459, 2.423, 2.391, 2.349, 2.325, 2.325, 2.344, 2.374, 2.412, 2.444, 2.476, 2.505, 2.519, + 2.543, 2.518, 2.479, 2.435, 2.392, 2.349, 2.324, 2.285, 2.283, 2.313, 2.344, 2.374, 2.417, 2.457, 2.489, 2.506, + 2.541, 2.511, 2.469, 2.421, 2.372, 2.326, 2.284, 2.277, 2.279, 2.283, 2.313, 2.357, 2.401, 2.443, 2.479, 2.504, + 2.541, 2.511, 2.469, 2.421, 2.372, 2.326, 2.284, 2.267, 2.267, 2.281, 2.313, 2.357, 2.401, 2.443, 2.479, 2.504, + 2.541, 2.512, 2.472, 2.425, 2.381, 2.338, 2.302, 2.278, 2.279, 2.301, 2.324, 2.364, 2.407, 2.447, 2.481, 2.504, + 2.544, 2.519, 2.483, 2.441, 2.401, 2.363, 2.338, 2.302, 2.302, 2.324, 2.355, 2.385, 2.423, 2.459, 2.488, 2.506, + 2.549, 2.527, 2.497, 2.463, 2.427, 2.401, 2.363, 2.345, 2.345, 2.355, 2.385, 2.412, 2.444, 2.473, 2.497, 2.509, + 2.552, 2.532, 2.507, 2.481, 2.459, 2.427, 2.402, 2.389, 2.389, 2.394, 2.412, 2.444, 2.465, 2.481, 2.499, 2.511, + 2.553, 2.533, 2.508, 2.489, 2.475, 2.454, 2.429, 2.429, 2.429, 2.429, 2.439, 2.463, 2.481, 2.492, 2.504, 2.511 + ] + } + ], + "calibrations_Cb": [ + { + "ct": 3000, + "table": + [ + 3.132, 3.126, 3.116, 3.103, 3.097, 3.091, 3.087, 3.086, 3.088, 3.091, 3.092, 3.102, 3.113, 3.121, 3.141, 3.144, + 3.149, 3.132, 3.123, 3.108, 3.101, 3.096, 3.091, 3.089, 3.091, 3.092, 3.101, 3.107, 3.116, 3.129, 3.144, 3.153, + 3.161, 3.149, 3.129, 3.121, 3.108, 3.103, 3.101, 3.101, 3.101, 3.103, 3.107, 3.116, 3.125, 3.134, 3.153, 3.159, + 3.176, 3.161, 3.144, 3.129, 3.124, 3.121, 3.117, 3.118, 3.118, 3.119, 3.122, 3.125, 3.134, 3.146, 3.159, 3.171, + 3.183, 3.176, 3.157, 3.144, 3.143, 3.143, 3.139, 3.141, 3.141, 3.141, 3.141, 3.141, 3.146, 3.161, 3.171, 3.179, + 3.189, 3.183, 3.165, 3.157, 3.156, 3.157, 3.159, 3.163, 3.163, 3.163, 3.163, 3.161, 3.163, 3.169, 3.179, 3.187, + 3.199, 3.189, 3.171, 3.165, 3.164, 3.167, 3.171, 3.173, 3.173, 3.172, 3.171, 3.169, 3.169, 3.175, 3.187, 3.189, + 3.206, 3.196, 3.177, 3.171, 3.165, 3.167, 3.171, 3.173, 3.173, 3.172, 3.171, 3.171, 3.173, 3.177, 3.192, 3.194, + 3.209, 3.197, 3.178, 3.171, 3.164, 3.161, 3.159, 3.161, 3.162, 3.164, 3.167, 3.171, 3.173, 3.181, 3.193, 3.198, + 3.204, 3.194, 3.176, 3.165, 3.161, 3.156, 3.154, 3.154, 3.159, 3.161, 3.164, 3.168, 3.173, 3.182, 3.198, 3.199, + 3.199, 3.191, 3.176, 3.169, 3.161, 3.157, 3.153, 3.153, 3.156, 3.161, 3.164, 3.168, 3.173, 3.186, 3.196, 3.199, + 3.199, 3.188, 3.179, 3.173, 3.165, 3.157, 3.153, 3.154, 3.156, 3.159, 3.167, 3.171, 3.176, 3.185, 3.193, 3.198 + ] + }, + { + "ct": 6000, + "table": + [ + 1.579, 1.579, 1.577, 1.574, 1.573, 1.571, 1.571, 1.571, 1.571, 1.569, 1.569, 1.571, 1.572, 1.574, 1.577, 1.578, + 1.584, 1.579, 1.578, 1.575, 1.573, 1.572, 1.571, 1.572, 1.572, 1.571, 1.571, 1.572, 1.573, 1.576, 1.578, 1.579, + 1.587, 1.584, 1.579, 1.578, 1.575, 1.573, 1.573, 1.575, 1.575, 1.574, 1.573, 1.574, 1.576, 1.578, 1.581, 1.581, + 1.591, 1.587, 1.584, 1.579, 1.578, 1.579, 1.579, 1.581, 1.581, 1.581, 1.578, 1.577, 1.578, 1.581, 1.585, 1.586, + 1.595, 1.591, 1.587, 1.585, 1.585, 1.586, 1.587, 1.587, 1.588, 1.588, 1.585, 1.584, 1.584, 1.586, 1.589, 1.589, + 1.597, 1.595, 1.591, 1.589, 1.591, 1.593, 1.595, 1.596, 1.597, 1.597, 1.595, 1.594, 1.592, 1.592, 1.593, 1.593, + 1.601, 1.597, 1.593, 1.592, 1.593, 1.595, 1.598, 1.599, 1.602, 1.601, 1.598, 1.596, 1.595, 1.596, 1.595, 1.595, + 1.601, 1.599, 1.594, 1.593, 1.593, 1.595, 1.598, 1.599, 1.602, 1.601, 1.598, 1.597, 1.597, 1.597, 1.597, 1.597, + 1.602, 1.599, 1.594, 1.593, 1.592, 1.593, 1.595, 1.597, 1.597, 1.598, 1.598, 1.597, 1.597, 1.597, 1.598, 1.598, + 1.599, 1.598, 1.594, 1.592, 1.591, 1.591, 1.592, 1.595, 1.596, 1.597, 1.597, 1.597, 1.597, 1.599, 1.599, 1.599, + 1.598, 1.596, 1.594, 1.593, 1.592, 1.592, 1.592, 1.594, 1.595, 1.597, 1.597, 1.597, 1.598, 1.599, 1.599, 1.599, + 1.597, 1.595, 1.594, 1.594, 1.593, 1.592, 1.593, 1.595, 1.595, 1.597, 1.598, 1.598, 1.598, 1.599, 1.599, 1.599 + ] + } + ], + "luminance_lut": + [ + 2.887, 2.754, 2.381, 2.105, 1.859, 1.678, 1.625, 1.623, 1.623, 1.624, 1.669, 1.849, 2.092, 2.362, 2.723, 2.838, + 2.754, 2.443, 2.111, 1.905, 1.678, 1.542, 1.455, 1.412, 1.412, 1.452, 1.535, 1.665, 1.893, 2.096, 2.413, 2.723, + 2.443, 2.216, 1.911, 1.678, 1.537, 1.372, 1.288, 1.245, 1.245, 1.283, 1.363, 1.527, 1.665, 1.895, 2.193, 2.413, + 2.318, 2.057, 1.764, 1.541, 1.372, 1.282, 1.159, 1.113, 1.113, 1.151, 1.269, 1.363, 1.527, 1.749, 2.034, 2.278, + 2.259, 1.953, 1.671, 1.452, 1.283, 1.159, 1.107, 1.018, 1.017, 1.097, 1.151, 1.269, 1.437, 1.655, 1.931, 2.222, + 2.257, 1.902, 1.624, 1.408, 1.239, 1.111, 1.019, 1.011, 1.005, 1.014, 1.098, 1.227, 1.395, 1.608, 1.883, 2.222, + 2.257, 1.902, 1.624, 1.408, 1.239, 1.111, 1.016, 1.001, 1.001, 1.007, 1.098, 1.227, 1.395, 1.608, 1.883, 2.222, + 2.257, 1.946, 1.666, 1.448, 1.281, 1.153, 1.093, 1.013, 1.008, 1.089, 1.143, 1.269, 1.437, 1.654, 1.934, 2.226, + 2.309, 2.044, 1.756, 1.532, 1.363, 1.259, 1.153, 1.093, 1.093, 1.143, 1.264, 1.354, 1.524, 1.746, 2.035, 2.284, + 2.425, 2.201, 1.896, 1.662, 1.519, 1.363, 1.259, 1.214, 1.214, 1.264, 1.354, 1.519, 1.655, 1.888, 2.191, 2.413, + 2.724, 2.417, 2.091, 1.888, 1.662, 1.519, 1.419, 1.373, 1.373, 1.425, 1.521, 1.655, 1.885, 2.089, 2.409, 2.722, + 2.858, 2.724, 2.356, 2.085, 1.842, 1.658, 1.581, 1.577, 1.577, 1.579, 1.653, 1.838, 2.084, 2.359, 2.722, 2.842 + ], + "sigma": 0.00372, + "sigma_Cb": 0.00244 + } + }, + { + "rpi.contrast": + { + "ce_enable": 1, + "gamma_curve": + [ + 0, 0, + 1024, 5040, + 2048, 9338, + 3072, 12356, + 4096, 15312, + 5120, 18051, + 6144, 20790, + 7168, 23193, + 8192, 25744, + 9216, 27942, + 10240, 30035, + 11264, 32005, + 12288, 33975, + 13312, 35815, + 14336, 37600, + 15360, 39168, + 16384, 40642, + 18432, 43379, + 20480, 45749, + 22528, 47753, + 24576, 49621, + 26624, 51253, + 28672, 52698, + 30720, 53796, + 32768, 54876, + 36864, 57012, + 40960, 58656, + 45056, 59954, + 49152, 61183, + 53248, 62355, + 57344, 63419, + 61440, 64476, + 65535, 65535 + ] + } + }, + { + "rpi.ccm": + { + "ccms": [ + { + "ct": 2890, + "ccm": + [ + 1.36754, -0.18448, -0.18306, + -0.32356, 1.44826, -0.12471, + -0.00412, -0.69936, 1.70348 + ] + }, + { + "ct": 2920, + "ccm": + [ + 1.26704, 0.01624, -0.28328, + -0.28516, 1.38934, -0.10419, + -0.04854, -0.82211, 1.87066 + ] + }, + { + "ct": 3550, + "ccm": + [ + 1.42836, -0.27235, -0.15601, + -0.28751, 1.41075, -0.12325, + -0.01812, -0.54849, 1.56661 + ] + }, + { + "ct": 4500, + "ccm": + [ + 1.36328, -0.19569, -0.16759, + -0.25254, 1.52248, -0.26994, + -0.01575, -0.53155, 1.54729 + ] + }, + { + "ct": 5700, + "ccm": + [ + 1.49207, -0.37245, -0.11963, + -0.21493, 1.40005, -0.18512, + -0.03781, -0.38779, 1.42561 + ] + }, + { + "ct": 7900, + "ccm": + [ + 1.34849, -0.05425, -0.29424, + -0.22182, 1.77684, -0.55502, + -0.07403, -0.55336, 1.62739 + ] + } + ] + } + }, + { + "rpi.sharpen": { } + } + ] +} \ No newline at end of file diff --git a/src/ipa/rpi/vc4/data/imx708.json b/src/ipa/rpi/vc4/data/imx708.json new file mode 100644 index 00000000..b9830a3b --- /dev/null +++ b/src/ipa/rpi/vc4/data/imx708.json @@ -0,0 +1,556 @@ +{ + "version": 2.0, + "target": "bcm2835", + "algorithms": [ + { + "rpi.black_level": + { + "black_level": 4096 + } + }, + { + "rpi.dpc": { } + }, + { + "rpi.lux": + { + "reference_shutter_speed": 10672, + "reference_gain": 1.12, + "reference_aperture": 1.0, + "reference_lux": 977, + "reference_Y": 8627 + } + }, + { + "rpi.noise": + { + "reference_constant": 16.0, + "reference_slope": 4.0 + } + }, + { + "rpi.geq": + { + "offset": 215, + "slope": 0.00287 + } + }, + { + "rpi.sdn": { } + }, + { + "rpi.awb": + { + "priors": [ + { + "lux": 0, + "prior": + [ + 2000, 1.0, + 3000, 0.0, + 13000, 0.0 + ] + }, + { + "lux": 800, + "prior": + [ + 2000, 0.0, + 6000, 2.0, + 13000, 2.0 + ] + }, + { + "lux": 1500, + "prior": + [ + 2000, 0.0, + 4000, 1.0, + 6000, 6.0, + 6500, 7.0, + 7000, 1.0, + 13000, 1.0 + ] + } + ], + "modes": + { + "auto": + { + "lo": 2500, + "hi": 8000 + }, + "incandescent": + { + "lo": 2500, + "hi": 3000 + }, + "tungsten": + { + "lo": 3000, + "hi": 3500 + }, + "fluorescent": + { + "lo": 4000, + "hi": 4700 + }, + "indoor": + { + "lo": 3000, + "hi": 5000 + }, + "daylight": + { + "lo": 5500, + "hi": 6500 + }, + "cloudy": + { + "lo": 7000, + "hi": 8600 + } + }, + "bayes": 1, + "ct_curve": + [ + 2498.0, 0.8733, 0.2606, + 2821.0, 0.7707, 0.3245, + 2925.0, 0.7338, 0.3499, + 2926.0, 0.7193, 0.3603, + 2951.0, 0.7144, 0.3639, + 2954.0, 0.7111, 0.3663, + 3578.0, 0.6038, 0.4516, + 3717.0, 0.5861, 0.4669, + 3784.0, 0.5786, 0.4737, + 4485.0, 0.5113, 0.5368, + 4615.0, 0.4994, 0.5486, + 4671.0, 0.4927, 0.5554, + 5753.0, 0.4274, 0.6246, + 5773.0, 0.4265, 0.6256, + 7433.0, 0.3723, 0.6881 + ], + "sensitivity_r": 1.05, + "sensitivity_b": 1.05, + "transverse_pos": 0.03148, + "transverse_neg": 0.03061 + } + }, + { + "rpi.agc": + { + "metering_modes": + { + "centre-weighted": + { + "weights": [ 3, 3, 3, 2, 2, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0 ] + }, + "spot": + { + "weights": [ 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ] + }, + "matrix": + { + "weights": [ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ] + } + }, + "exposure_modes": + { + "normal": + { + "shutter": [ 100, 15000, 30000, 60000, 120000 ], + "gain": [ 1.0, 1.0, 2.0, 4.0, 6.0 ] + }, + "short": + { + "shutter": [ 100, 5000, 10000, 20000, 120000 ], + "gain": [ 1.0, 2.0, 4.0, 6.0, 6.0 ] + } + }, + "constraint_modes": + { + "normal": [ + { + "bound": "LOWER", + "q_lo": 0.98, + "q_hi": 1.0, + "y_target": + [ + 0, 0.2, + 1000, 0.2 + ] + } + ], + "highlight": [ + { + "bound": "LOWER", + "q_lo": 0.98, + "q_hi": 1.0, + "y_target": + [ + 0, 0.2, + 1000, 0.2 + ] + }, + { + "bound": "UPPER", + "q_lo": 0.98, + "q_hi": 1.0, + "y_target": + [ + 0, 0.8, + 1000, 0.8 + ] + } + ] + }, + "y_target": + [ + 0, 0.16, + 1000, 0.165, + 10000, 0.17 + ], + "startup_frames": 5, + "convergence_frames": 6, + "speed": 0.15 + } + }, + { + "rpi.alsc": + { + "omega": 1.3, + "n_iter": 100, + "luminance_strength": 0.5, + "calibrations_Cr": [ + { + "ct": 3000, + "table": + [ + 1.562, 1.566, 1.566, 1.556, 1.533, 1.506, 1.475, 1.475, 1.475, 1.475, 1.506, 1.533, 1.555, 1.563, 1.562, 1.555, + 1.563, 1.564, 1.561, 1.538, 1.508, 1.482, 1.449, 1.436, 1.436, 1.449, 1.481, 1.508, 1.537, 1.557, 1.558, 1.557, + 1.564, 1.563, 1.554, 1.522, 1.482, 1.449, 1.421, 1.403, 1.403, 1.419, 1.449, 1.481, 1.519, 1.549, 1.557, 1.559, + 1.564, 1.563, 1.545, 1.506, 1.462, 1.421, 1.403, 1.378, 1.378, 1.402, 1.419, 1.459, 1.503, 1.541, 1.557, 1.559, + 1.564, 1.562, 1.537, 1.494, 1.447, 1.404, 1.378, 1.364, 1.364, 1.377, 1.402, 1.444, 1.491, 1.532, 1.556, 1.559, + 1.564, 1.559, 1.532, 1.487, 1.438, 1.395, 1.365, 1.359, 1.359, 1.364, 1.393, 1.436, 1.484, 1.527, 1.555, 1.558, + 1.564, 1.559, 1.532, 1.487, 1.438, 1.395, 1.365, 1.356, 1.356, 1.364, 1.393, 1.436, 1.484, 1.527, 1.554, 1.557, + 1.564, 1.561, 1.536, 1.492, 1.444, 1.402, 1.374, 1.364, 1.363, 1.373, 1.401, 1.442, 1.489, 1.531, 1.554, 1.557, + 1.564, 1.563, 1.544, 1.504, 1.458, 1.418, 1.397, 1.374, 1.374, 1.395, 1.416, 1.456, 1.501, 1.538, 1.556, 1.557, + 1.564, 1.562, 1.551, 1.518, 1.477, 1.441, 1.418, 1.397, 1.397, 1.416, 1.438, 1.474, 1.514, 1.546, 1.556, 1.556, + 1.562, 1.562, 1.558, 1.534, 1.499, 1.476, 1.441, 1.426, 1.426, 1.438, 1.473, 1.496, 1.531, 1.552, 1.556, 1.555, + 1.561, 1.564, 1.564, 1.552, 1.525, 1.497, 1.466, 1.461, 1.461, 1.464, 1.495, 1.523, 1.548, 1.556, 1.556, 1.552 + ] + }, + { + "ct": 5000, + "table": + [ + 2.609, 2.616, 2.617, 2.607, 2.573, 2.527, 2.483, 2.481, 2.481, 2.483, 2.529, 2.573, 2.604, 2.613, 2.613, 2.604, + 2.609, 2.615, 2.608, 2.576, 2.533, 2.489, 2.439, 2.418, 2.418, 2.439, 2.491, 2.532, 2.577, 2.605, 2.609, 2.607, + 2.611, 2.611, 2.597, 2.551, 2.489, 2.439, 2.391, 2.364, 2.364, 2.391, 2.439, 2.491, 2.551, 2.592, 2.607, 2.609, + 2.612, 2.608, 2.583, 2.526, 2.457, 2.391, 2.362, 2.318, 2.318, 2.362, 2.391, 2.458, 2.526, 2.581, 2.607, 2.611, + 2.612, 2.604, 2.571, 2.507, 2.435, 2.362, 2.317, 2.293, 2.294, 2.318, 2.363, 2.434, 2.508, 2.568, 2.604, 2.612, + 2.611, 2.602, 2.564, 2.496, 2.419, 2.349, 2.293, 2.284, 2.284, 2.294, 2.347, 2.421, 2.497, 2.562, 2.603, 2.611, + 2.609, 2.601, 2.564, 2.496, 2.419, 2.349, 2.293, 2.278, 2.278, 2.294, 2.347, 2.421, 2.497, 2.562, 2.602, 2.609, + 2.609, 2.602, 2.568, 2.503, 2.429, 2.361, 2.311, 2.292, 2.292, 2.309, 2.357, 2.429, 2.504, 2.567, 2.602, 2.609, + 2.606, 2.604, 2.579, 2.519, 2.449, 2.384, 2.348, 2.311, 2.311, 2.346, 2.383, 2.449, 2.521, 2.577, 2.604, 2.608, + 2.604, 2.603, 2.586, 2.537, 2.474, 2.418, 2.384, 2.348, 2.348, 2.383, 2.417, 2.476, 2.538, 2.586, 2.601, 2.603, + 2.603, 2.605, 2.596, 2.561, 2.508, 2.474, 2.418, 2.396, 2.396, 2.417, 2.474, 2.511, 2.562, 2.596, 2.603, 2.602, + 2.601, 2.607, 2.606, 2.589, 2.549, 2.507, 2.456, 2.454, 2.454, 2.458, 2.508, 2.554, 2.594, 2.605, 2.605, 2.602 + ] + } + ], + "calibrations_Cb": [ + { + "ct": 3000, + "table": + [ + 3.221, 3.226, 3.231, 3.236, 3.239, 3.243, 3.245, 3.247, 3.249, 3.253, 3.255, 3.254, 3.253, 3.242, 3.235, 3.226, + 3.225, 3.231, 3.235, 3.238, 3.241, 3.244, 3.246, 3.247, 3.249, 3.254, 3.256, 3.255, 3.252, 3.248, 3.241, 3.232, + 3.226, 3.234, 3.239, 3.243, 3.243, 3.245, 3.247, 3.248, 3.251, 3.255, 3.256, 3.256, 3.254, 3.249, 3.244, 3.236, + 3.232, 3.238, 3.245, 3.245, 3.246, 3.247, 3.248, 3.251, 3.251, 3.256, 3.257, 3.257, 3.256, 3.254, 3.249, 3.239, + 3.232, 3.243, 3.246, 3.246, 3.246, 3.247, 3.248, 3.251, 3.253, 3.257, 3.258, 3.258, 3.257, 3.256, 3.254, 3.239, + 3.232, 3.242, 3.246, 3.247, 3.246, 3.246, 3.248, 3.251, 3.252, 3.253, 3.256, 3.255, 3.255, 3.254, 3.251, 3.239, + 3.233, 3.241, 3.244, 3.245, 3.244, 3.245, 3.246, 3.249, 3.251, 3.252, 3.253, 3.252, 3.252, 3.252, 3.249, 3.238, + 3.238, 3.241, 3.246, 3.246, 3.245, 3.245, 3.247, 3.249, 3.251, 3.252, 3.253, 3.253, 3.252, 3.252, 3.249, 3.239, + 3.235, 3.241, 3.245, 3.245, 3.245, 3.245, 3.246, 3.247, 3.251, 3.254, 3.253, 3.255, 3.256, 3.255, 3.251, 3.241, + 3.226, 3.235, 3.241, 3.241, 3.241, 3.241, 3.243, 3.245, 3.246, 3.252, 3.253, 3.254, 3.256, 3.254, 3.241, 3.237, + 3.205, 3.213, 3.213, 3.214, 3.214, 3.214, 3.214, 3.213, 3.213, 3.216, 3.218, 3.216, 3.214, 3.213, 3.211, 3.208, + 3.205, 3.205, 3.212, 3.212, 3.212, 3.213, 3.211, 3.211, 3.211, 3.213, 3.216, 3.214, 3.213, 3.211, 3.208, 3.196 + ] + }, + { + "ct": 5000, + "table": + [ + 1.645, 1.646, 1.649, 1.653, 1.654, 1.657, 1.659, 1.661, 1.663, 1.662, 1.661, 1.659, 1.656, 1.651, 1.645, 1.642, + 1.646, 1.649, 1.652, 1.654, 1.656, 1.659, 1.662, 1.663, 1.664, 1.664, 1.662, 1.661, 1.657, 1.653, 1.649, 1.644, + 1.648, 1.652, 1.654, 1.656, 1.658, 1.662, 1.665, 1.668, 1.668, 1.668, 1.665, 1.662, 1.658, 1.655, 1.652, 1.646, + 1.649, 1.653, 1.656, 1.658, 1.661, 1.665, 1.667, 1.671, 1.673, 1.671, 1.668, 1.663, 1.659, 1.656, 1.654, 1.647, + 1.649, 1.655, 1.657, 1.659, 1.661, 1.666, 1.671, 1.674, 1.675, 1.673, 1.671, 1.664, 1.659, 1.656, 1.654, 1.648, + 1.649, 1.654, 1.656, 1.659, 1.661, 1.666, 1.673, 1.676, 1.676, 1.675, 1.671, 1.664, 1.659, 1.656, 1.654, 1.648, + 1.649, 1.654, 1.656, 1.658, 1.659, 1.665, 1.672, 1.675, 1.675, 1.674, 1.668, 1.662, 1.658, 1.655, 1.654, 1.646, + 1.652, 1.655, 1.657, 1.659, 1.661, 1.665, 1.671, 1.673, 1.673, 1.672, 1.668, 1.662, 1.658, 1.655, 1.654, 1.647, + 1.652, 1.655, 1.657, 1.659, 1.661, 1.664, 1.667, 1.671, 1.672, 1.668, 1.666, 1.662, 1.659, 1.656, 1.654, 1.647, + 1.647, 1.652, 1.655, 1.656, 1.657, 1.661, 1.664, 1.665, 1.665, 1.665, 1.663, 1.661, 1.657, 1.655, 1.647, 1.647, + 1.639, 1.642, 1.644, 1.645, 1.646, 1.648, 1.648, 1.648, 1.649, 1.649, 1.649, 1.646, 1.645, 1.642, 1.639, 1.636, + 1.639, 1.641, 1.642, 1.644, 1.645, 1.646, 1.647, 1.647, 1.648, 1.648, 1.647, 1.645, 1.642, 1.639, 1.636, 1.633 + ] + } + ], + "luminance_lut": + [ + 2.644, 2.396, 2.077, 1.863, 1.682, 1.535, 1.392, 1.382, 1.382, 1.382, 1.515, 1.657, 1.826, 2.035, 2.351, 2.604, + 2.497, 2.229, 1.947, 1.733, 1.539, 1.424, 1.296, 1.249, 1.249, 1.285, 1.401, 1.519, 1.699, 1.908, 2.183, 2.456, + 2.389, 2.109, 1.848, 1.622, 1.424, 1.296, 1.201, 1.146, 1.146, 1.188, 1.285, 1.401, 1.591, 1.811, 2.065, 2.347, + 2.317, 2.026, 1.771, 1.535, 1.339, 1.201, 1.145, 1.069, 1.069, 1.134, 1.188, 1.318, 1.505, 1.734, 1.983, 2.273, + 2.276, 1.972, 1.715, 1.474, 1.281, 1.148, 1.069, 1.033, 1.024, 1.065, 1.134, 1.262, 1.446, 1.679, 1.929, 2.233, + 2.268, 1.941, 1.682, 1.441, 1.251, 1.119, 1.033, 1.013, 1.013, 1.024, 1.105, 1.231, 1.415, 1.649, 1.898, 2.227, + 2.268, 1.941, 1.682, 1.441, 1.251, 1.119, 1.033, 1.001, 1.001, 1.024, 1.105, 1.231, 1.415, 1.649, 1.898, 2.227, + 2.268, 1.951, 1.694, 1.456, 1.265, 1.131, 1.044, 1.026, 1.019, 1.039, 1.118, 1.246, 1.429, 1.663, 1.912, 2.227, + 2.291, 1.992, 1.738, 1.505, 1.311, 1.175, 1.108, 1.044, 1.041, 1.106, 1.161, 1.292, 1.478, 1.707, 1.955, 2.252, + 2.347, 2.058, 1.803, 1.581, 1.384, 1.245, 1.175, 1.108, 1.108, 1.161, 1.239, 1.364, 1.551, 1.773, 2.023, 2.311, + 2.438, 2.156, 1.884, 1.674, 1.484, 1.373, 1.245, 1.199, 1.199, 1.239, 1.363, 1.463, 1.647, 1.858, 2.123, 2.406, + 2.563, 2.305, 1.998, 1.792, 1.615, 1.472, 1.339, 1.322, 1.322, 1.326, 1.456, 1.593, 1.767, 1.973, 2.273, 2.532 + ], + "sigma": 0.00178, + "sigma_Cb": 0.00217 + } + }, + { + "rpi.contrast": + { + "ce_enable": 1, + "gamma_curve": + [ + 0, 0, + 1024, 5040, + 2048, 9338, + 3072, 12356, + 4096, 15312, + 5120, 18051, + 6144, 20790, + 7168, 23193, + 8192, 25744, + 9216, 27942, + 10240, 30035, + 11264, 32005, + 12288, 33975, + 13312, 35815, + 14336, 37600, + 15360, 39168, + 16384, 40642, + 18432, 43379, + 20480, 45749, + 22528, 47753, + 24576, 49621, + 26624, 51253, + 28672, 52698, + 30720, 53796, + 32768, 54876, + 36864, 57012, + 40960, 58656, + 45056, 59954, + 49152, 61183, + 53248, 62355, + 57344, 63419, + 61440, 64476, + 65535, 65535 + ] + } + }, + { + "rpi.ccm": + { + "ccms": [ + { + "ct": 2498, + "ccm": + [ + 1.14912, 0.28638, -0.43551, + -0.49691, 1.60391, -0.10701, + -0.10513, -1.09534, 2.20047 + ] + }, + { + "ct": 2821, + "ccm": + [ + 1.18251, 0.15501, -0.33752, + -0.44304, 1.58495, -0.14191, + -0.05077, -0.96422, 2.01498 + ] + }, + { + "ct": 2925, + "ccm": + [ + 1.18668, 0.00195, -0.18864, + -0.41617, 1.50514, -0.08897, + -0.02675, -0.91143, 1.93818 + ] + }, + { + "ct": 2926, + "ccm": + [ + 1.50948, -0.44421, -0.06527, + -0.37241, 1.41726, -0.04486, + 0.07098, -0.84694, 1.77596 + ] + }, + { + "ct": 2951, + "ccm": + [ + 1.52743, -0.47333, -0.05411, + -0.36485, 1.40764, -0.04279, + 0.08672, -0.90479, 1.81807 + ] + }, + { + "ct": 2954, + "ccm": + [ + 1.51683, -0.46841, -0.04841, + -0.36288, 1.39914, -0.03625, + 0.06421, -0.82034, 1.75613 + ] + }, + { + "ct": 3578, + "ccm": + [ + 1.59888, -0.59105, -0.00784, + -0.29366, 1.32037, -0.02671, + 0.06627, -0.76465, 1.69838 + ] + }, + { + "ct": 3717, + "ccm": + [ + 1.59063, -0.58059, -0.01003, + -0.29583, 1.32715, -0.03132, + 0.03613, -0.67431, 1.63817 + ] + }, + { + "ct": 3784, + "ccm": + [ + 1.59379, -0.58861, -0.00517, + -0.29178, 1.33292, -0.04115, + 0.03541, -0.66162, 1.62622 + ] + }, + { + "ct": 4485, + "ccm": + [ + 1.40761, -0.34561, -0.06201, + -0.32388, 1.57221, -0.24832, + -0.01014, -0.63427, 1.64441 + ] + }, + { + "ct": 4615, + "ccm": + [ + 1.41537, -0.35832, -0.05705, + -0.31429, 1.56019, -0.24591, + -0.01761, -0.61859, 1.63621 + ] + }, + { + "ct": 4671, + "ccm": + [ + 1.42941, -0.38178, -0.04764, + -0.31421, 1.55925, -0.24504, + -0.01141, -0.62987, 1.64129 + ] + }, + { + "ct": 5753, + "ccm": + [ + 1.64549, -0.63329, -0.01221, + -0.22431, 1.36423, -0.13992, + -0.00831, -0.55373, 1.56204 + ] + }, + { + "ct": 5773, + "ccm": + [ + 1.63668, -0.63557, -0.00111, + -0.21919, 1.36234, -0.14315, + -0.00399, -0.57428, 1.57827 + ] + }, + { + "ct": 7433, + "ccm": + [ + 1.36007, -0.09277, -0.26729, + -0.36886, 2.09249, -0.72363, + -0.12573, -0.76761, 1.89334 + ] + }, + { + "ct": 55792, + "ccm": + [ + 1.65091, -0.63689, -0.01401, + -0.22277, 1.35752, -0.13475, + -0.00943, -0.55091, 1.56033 + ] + } + ] + } + }, + { + "rpi.sharpen": { } + }, + { + "rpi.af": + { + "ranges": + { + "normal": + { + "min": 0.0, + "max": 12.0, + "default": 1.0 + }, + "macro": + { + "min": 3.0, + "max": 15.0, + "default": 4.0 + } + }, + "speeds": + { + "normal": + { + "step_coarse": 1.0, + "step_fine": 0.25, + "contrast_ratio": 0.75, + "pdaf_gain": -0.02, + "pdaf_squelch": 0.125, + "max_slew": 2.0, + "pdaf_frames": 20, + "dropout_frames": 6, + "step_frames": 4 + } + }, + "conf_epsilon": 8, + "conf_thresh": 16, + "conf_clip": 512, + "skip_frames": 5, + "map": [ 0.0, 445, 15.0, 925 ] + } + } + ] +} diff --git a/src/ipa/rpi/vc4/data/imx708_noir.json b/src/ipa/rpi/vc4/data/imx708_noir.json new file mode 100644 index 00000000..075f7035 --- /dev/null +++ b/src/ipa/rpi/vc4/data/imx708_noir.json @@ -0,0 +1,556 @@ +{ + "version": 2.0, + "target": "bcm2835", + "algorithms": [ + { + "rpi.black_level": + { + "black_level": 4096 + } + }, + { + "rpi.dpc": { } + }, + { + "rpi.lux": + { + "reference_shutter_speed": 10672, + "reference_gain": 1.12, + "reference_aperture": 1.0, + "reference_lux": 977, + "reference_Y": 8627 + } + }, + { + "rpi.noise": + { + "reference_constant": 16.0, + "reference_slope": 4.0 + } + }, + { + "rpi.geq": + { + "offset": 215, + "slope": 0.00287 + } + }, + { + "rpi.sdn": { } + }, + { + "rpi.awb": + { + "priors": [ + { + "lux": 0, + "prior": + [ + 2000, 1.0, + 3000, 0.0, + 13000, 0.0 + ] + }, + { + "lux": 800, + "prior": + [ + 2000, 0.0, + 6000, 2.0, + 13000, 2.0 + ] + }, + { + "lux": 1500, + "prior": + [ + 2000, 0.0, + 4000, 1.0, + 6000, 6.0, + 6500, 7.0, + 7000, 1.0, + 13000, 1.0 + ] + } + ], + "modes": + { + "auto": + { + "lo": 2500, + "hi": 8000 + }, + "incandescent": + { + "lo": 2500, + "hi": 3000 + }, + "tungsten": + { + "lo": 3000, + "hi": 3500 + }, + "fluorescent": + { + "lo": 4000, + "hi": 4700 + }, + "indoor": + { + "lo": 3000, + "hi": 5000 + }, + "daylight": + { + "lo": 5500, + "hi": 6500 + }, + "cloudy": + { + "lo": 7000, + "hi": 8600 + } + }, + "bayes": 0, + "ct_curve": + [ + 2498.0, 0.8733, 0.2606, + 2821.0, 0.7707, 0.3245, + 2925.0, 0.7338, 0.3499, + 2926.0, 0.7193, 0.3603, + 2951.0, 0.7144, 0.3639, + 2954.0, 0.7111, 0.3663, + 3578.0, 0.6038, 0.4516, + 3717.0, 0.5861, 0.4669, + 3784.0, 0.5786, 0.4737, + 4485.0, 0.5113, 0.5368, + 4615.0, 0.4994, 0.5486, + 4671.0, 0.4927, 0.5554, + 5753.0, 0.4274, 0.6246, + 5773.0, 0.4265, 0.6256, + 7433.0, 0.3723, 0.6881 + ], + "sensitivity_r": 1.05, + "sensitivity_b": 1.05, + "transverse_pos": 0.03148, + "transverse_neg": 0.03061 + } + }, + { + "rpi.agc": + { + "metering_modes": + { + "centre-weighted": + { + "weights": [ 3, 3, 3, 2, 2, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0 ] + }, + "spot": + { + "weights": [ 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ] + }, + "matrix": + { + "weights": [ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ] + } + }, + "exposure_modes": + { + "normal": + { + "shutter": [ 100, 15000, 30000, 60000, 120000 ], + "gain": [ 1.0, 1.0, 2.0, 4.0, 6.0 ] + }, + "short": + { + "shutter": [ 100, 5000, 10000, 20000, 120000 ], + "gain": [ 1.0, 2.0, 4.0, 6.0, 6.0 ] + } + }, + "constraint_modes": + { + "normal": [ + { + "bound": "LOWER", + "q_lo": 0.98, + "q_hi": 1.0, + "y_target": + [ + 0, 0.2, + 1000, 0.2 + ] + } + ], + "highlight": [ + { + "bound": "LOWER", + "q_lo": 0.98, + "q_hi": 1.0, + "y_target": + [ + 0, 0.2, + 1000, 0.2 + ] + }, + { + "bound": "UPPER", + "q_lo": 0.98, + "q_hi": 1.0, + "y_target": + [ + 0, 0.8, + 1000, 0.8 + ] + } + ] + }, + "y_target": + [ + 0, 0.16, + 1000, 0.165, + 10000, 0.17 + ], + "startup_frames": 5, + "convergence_frames": 6, + "speed": 0.15 + } + }, + { + "rpi.alsc": + { + "omega": 1.3, + "n_iter": 100, + "luminance_strength": 0.5, + "calibrations_Cr": [ + { + "ct": 3000, + "table": + [ + 1.562, 1.566, 1.566, 1.556, 1.533, 1.506, 1.475, 1.475, 1.475, 1.475, 1.506, 1.533, 1.555, 1.563, 1.562, 1.555, + 1.563, 1.564, 1.561, 1.538, 1.508, 1.482, 1.449, 1.436, 1.436, 1.449, 1.481, 1.508, 1.537, 1.557, 1.558, 1.557, + 1.564, 1.563, 1.554, 1.522, 1.482, 1.449, 1.421, 1.403, 1.403, 1.419, 1.449, 1.481, 1.519, 1.549, 1.557, 1.559, + 1.564, 1.563, 1.545, 1.506, 1.462, 1.421, 1.403, 1.378, 1.378, 1.402, 1.419, 1.459, 1.503, 1.541, 1.557, 1.559, + 1.564, 1.562, 1.537, 1.494, 1.447, 1.404, 1.378, 1.364, 1.364, 1.377, 1.402, 1.444, 1.491, 1.532, 1.556, 1.559, + 1.564, 1.559, 1.532, 1.487, 1.438, 1.395, 1.365, 1.359, 1.359, 1.364, 1.393, 1.436, 1.484, 1.527, 1.555, 1.558, + 1.564, 1.559, 1.532, 1.487, 1.438, 1.395, 1.365, 1.356, 1.356, 1.364, 1.393, 1.436, 1.484, 1.527, 1.554, 1.557, + 1.564, 1.561, 1.536, 1.492, 1.444, 1.402, 1.374, 1.364, 1.363, 1.373, 1.401, 1.442, 1.489, 1.531, 1.554, 1.557, + 1.564, 1.563, 1.544, 1.504, 1.458, 1.418, 1.397, 1.374, 1.374, 1.395, 1.416, 1.456, 1.501, 1.538, 1.556, 1.557, + 1.564, 1.562, 1.551, 1.518, 1.477, 1.441, 1.418, 1.397, 1.397, 1.416, 1.438, 1.474, 1.514, 1.546, 1.556, 1.556, + 1.562, 1.562, 1.558, 1.534, 1.499, 1.476, 1.441, 1.426, 1.426, 1.438, 1.473, 1.496, 1.531, 1.552, 1.556, 1.555, + 1.561, 1.564, 1.564, 1.552, 1.525, 1.497, 1.466, 1.461, 1.461, 1.464, 1.495, 1.523, 1.548, 1.556, 1.556, 1.552 + ] + }, + { + "ct": 5000, + "table": + [ + 2.609, 2.616, 2.617, 2.607, 2.573, 2.527, 2.483, 2.481, 2.481, 2.483, 2.529, 2.573, 2.604, 2.613, 2.613, 2.604, + 2.609, 2.615, 2.608, 2.576, 2.533, 2.489, 2.439, 2.418, 2.418, 2.439, 2.491, 2.532, 2.577, 2.605, 2.609, 2.607, + 2.611, 2.611, 2.597, 2.551, 2.489, 2.439, 2.391, 2.364, 2.364, 2.391, 2.439, 2.491, 2.551, 2.592, 2.607, 2.609, + 2.612, 2.608, 2.583, 2.526, 2.457, 2.391, 2.362, 2.318, 2.318, 2.362, 2.391, 2.458, 2.526, 2.581, 2.607, 2.611, + 2.612, 2.604, 2.571, 2.507, 2.435, 2.362, 2.317, 2.293, 2.294, 2.318, 2.363, 2.434, 2.508, 2.568, 2.604, 2.612, + 2.611, 2.602, 2.564, 2.496, 2.419, 2.349, 2.293, 2.284, 2.284, 2.294, 2.347, 2.421, 2.497, 2.562, 2.603, 2.611, + 2.609, 2.601, 2.564, 2.496, 2.419, 2.349, 2.293, 2.278, 2.278, 2.294, 2.347, 2.421, 2.497, 2.562, 2.602, 2.609, + 2.609, 2.602, 2.568, 2.503, 2.429, 2.361, 2.311, 2.292, 2.292, 2.309, 2.357, 2.429, 2.504, 2.567, 2.602, 2.609, + 2.606, 2.604, 2.579, 2.519, 2.449, 2.384, 2.348, 2.311, 2.311, 2.346, 2.383, 2.449, 2.521, 2.577, 2.604, 2.608, + 2.604, 2.603, 2.586, 2.537, 2.474, 2.418, 2.384, 2.348, 2.348, 2.383, 2.417, 2.476, 2.538, 2.586, 2.601, 2.603, + 2.603, 2.605, 2.596, 2.561, 2.508, 2.474, 2.418, 2.396, 2.396, 2.417, 2.474, 2.511, 2.562, 2.596, 2.603, 2.602, + 2.601, 2.607, 2.606, 2.589, 2.549, 2.507, 2.456, 2.454, 2.454, 2.458, 2.508, 2.554, 2.594, 2.605, 2.605, 2.602 + ] + } + ], + "calibrations_Cb": [ + { + "ct": 3000, + "table": + [ + 3.221, 3.226, 3.231, 3.236, 3.239, 3.243, 3.245, 3.247, 3.249, 3.253, 3.255, 3.254, 3.253, 3.242, 3.235, 3.226, + 3.225, 3.231, 3.235, 3.238, 3.241, 3.244, 3.246, 3.247, 3.249, 3.254, 3.256, 3.255, 3.252, 3.248, 3.241, 3.232, + 3.226, 3.234, 3.239, 3.243, 3.243, 3.245, 3.247, 3.248, 3.251, 3.255, 3.256, 3.256, 3.254, 3.249, 3.244, 3.236, + 3.232, 3.238, 3.245, 3.245, 3.246, 3.247, 3.248, 3.251, 3.251, 3.256, 3.257, 3.257, 3.256, 3.254, 3.249, 3.239, + 3.232, 3.243, 3.246, 3.246, 3.246, 3.247, 3.248, 3.251, 3.253, 3.257, 3.258, 3.258, 3.257, 3.256, 3.254, 3.239, + 3.232, 3.242, 3.246, 3.247, 3.246, 3.246, 3.248, 3.251, 3.252, 3.253, 3.256, 3.255, 3.255, 3.254, 3.251, 3.239, + 3.233, 3.241, 3.244, 3.245, 3.244, 3.245, 3.246, 3.249, 3.251, 3.252, 3.253, 3.252, 3.252, 3.252, 3.249, 3.238, + 3.238, 3.241, 3.246, 3.246, 3.245, 3.245, 3.247, 3.249, 3.251, 3.252, 3.253, 3.253, 3.252, 3.252, 3.249, 3.239, + 3.235, 3.241, 3.245, 3.245, 3.245, 3.245, 3.246, 3.247, 3.251, 3.254, 3.253, 3.255, 3.256, 3.255, 3.251, 3.241, + 3.226, 3.235, 3.241, 3.241, 3.241, 3.241, 3.243, 3.245, 3.246, 3.252, 3.253, 3.254, 3.256, 3.254, 3.241, 3.237, + 3.205, 3.213, 3.213, 3.214, 3.214, 3.214, 3.214, 3.213, 3.213, 3.216, 3.218, 3.216, 3.214, 3.213, 3.211, 3.208, + 3.205, 3.205, 3.212, 3.212, 3.212, 3.213, 3.211, 3.211, 3.211, 3.213, 3.216, 3.214, 3.213, 3.211, 3.208, 3.196 + ] + }, + { + "ct": 5000, + "table": + [ + 1.645, 1.646, 1.649, 1.653, 1.654, 1.657, 1.659, 1.661, 1.663, 1.662, 1.661, 1.659, 1.656, 1.651, 1.645, 1.642, + 1.646, 1.649, 1.652, 1.654, 1.656, 1.659, 1.662, 1.663, 1.664, 1.664, 1.662, 1.661, 1.657, 1.653, 1.649, 1.644, + 1.648, 1.652, 1.654, 1.656, 1.658, 1.662, 1.665, 1.668, 1.668, 1.668, 1.665, 1.662, 1.658, 1.655, 1.652, 1.646, + 1.649, 1.653, 1.656, 1.658, 1.661, 1.665, 1.667, 1.671, 1.673, 1.671, 1.668, 1.663, 1.659, 1.656, 1.654, 1.647, + 1.649, 1.655, 1.657, 1.659, 1.661, 1.666, 1.671, 1.674, 1.675, 1.673, 1.671, 1.664, 1.659, 1.656, 1.654, 1.648, + 1.649, 1.654, 1.656, 1.659, 1.661, 1.666, 1.673, 1.676, 1.676, 1.675, 1.671, 1.664, 1.659, 1.656, 1.654, 1.648, + 1.649, 1.654, 1.656, 1.658, 1.659, 1.665, 1.672, 1.675, 1.675, 1.674, 1.668, 1.662, 1.658, 1.655, 1.654, 1.646, + 1.652, 1.655, 1.657, 1.659, 1.661, 1.665, 1.671, 1.673, 1.673, 1.672, 1.668, 1.662, 1.658, 1.655, 1.654, 1.647, + 1.652, 1.655, 1.657, 1.659, 1.661, 1.664, 1.667, 1.671, 1.672, 1.668, 1.666, 1.662, 1.659, 1.656, 1.654, 1.647, + 1.647, 1.652, 1.655, 1.656, 1.657, 1.661, 1.664, 1.665, 1.665, 1.665, 1.663, 1.661, 1.657, 1.655, 1.647, 1.647, + 1.639, 1.642, 1.644, 1.645, 1.646, 1.648, 1.648, 1.648, 1.649, 1.649, 1.649, 1.646, 1.645, 1.642, 1.639, 1.636, + 1.639, 1.641, 1.642, 1.644, 1.645, 1.646, 1.647, 1.647, 1.648, 1.648, 1.647, 1.645, 1.642, 1.639, 1.636, 1.633 + ] + } + ], + "luminance_lut": + [ + 2.644, 2.396, 2.077, 1.863, 1.682, 1.535, 1.392, 1.382, 1.382, 1.382, 1.515, 1.657, 1.826, 2.035, 2.351, 2.604, + 2.497, 2.229, 1.947, 1.733, 1.539, 1.424, 1.296, 1.249, 1.249, 1.285, 1.401, 1.519, 1.699, 1.908, 2.183, 2.456, + 2.389, 2.109, 1.848, 1.622, 1.424, 1.296, 1.201, 1.146, 1.146, 1.188, 1.285, 1.401, 1.591, 1.811, 2.065, 2.347, + 2.317, 2.026, 1.771, 1.535, 1.339, 1.201, 1.145, 1.069, 1.069, 1.134, 1.188, 1.318, 1.505, 1.734, 1.983, 2.273, + 2.276, 1.972, 1.715, 1.474, 1.281, 1.148, 1.069, 1.033, 1.024, 1.065, 1.134, 1.262, 1.446, 1.679, 1.929, 2.233, + 2.268, 1.941, 1.682, 1.441, 1.251, 1.119, 1.033, 1.013, 1.013, 1.024, 1.105, 1.231, 1.415, 1.649, 1.898, 2.227, + 2.268, 1.941, 1.682, 1.441, 1.251, 1.119, 1.033, 1.001, 1.001, 1.024, 1.105, 1.231, 1.415, 1.649, 1.898, 2.227, + 2.268, 1.951, 1.694, 1.456, 1.265, 1.131, 1.044, 1.026, 1.019, 1.039, 1.118, 1.246, 1.429, 1.663, 1.912, 2.227, + 2.291, 1.992, 1.738, 1.505, 1.311, 1.175, 1.108, 1.044, 1.041, 1.106, 1.161, 1.292, 1.478, 1.707, 1.955, 2.252, + 2.347, 2.058, 1.803, 1.581, 1.384, 1.245, 1.175, 1.108, 1.108, 1.161, 1.239, 1.364, 1.551, 1.773, 2.023, 2.311, + 2.438, 2.156, 1.884, 1.674, 1.484, 1.373, 1.245, 1.199, 1.199, 1.239, 1.363, 1.463, 1.647, 1.858, 2.123, 2.406, + 2.563, 2.305, 1.998, 1.792, 1.615, 1.472, 1.339, 1.322, 1.322, 1.326, 1.456, 1.593, 1.767, 1.973, 2.273, 2.532 + ], + "sigma": 0.00178, + "sigma_Cb": 0.00217 + } + }, + { + "rpi.contrast": + { + "ce_enable": 1, + "gamma_curve": + [ + 0, 0, + 1024, 5040, + 2048, 9338, + 3072, 12356, + 4096, 15312, + 5120, 18051, + 6144, 20790, + 7168, 23193, + 8192, 25744, + 9216, 27942, + 10240, 30035, + 11264, 32005, + 12288, 33975, + 13312, 35815, + 14336, 37600, + 15360, 39168, + 16384, 40642, + 18432, 43379, + 20480, 45749, + 22528, 47753, + 24576, 49621, + 26624, 51253, + 28672, 52698, + 30720, 53796, + 32768, 54876, + 36864, 57012, + 40960, 58656, + 45056, 59954, + 49152, 61183, + 53248, 62355, + 57344, 63419, + 61440, 64476, + 65535, 65535 + ] + } + }, + { + "rpi.ccm": + { + "ccms": [ + { + "ct": 2498, + "ccm": + [ + 1.14912, 0.28638, -0.43551, + -0.49691, 1.60391, -0.10701, + -0.10513, -1.09534, 2.20047 + ] + }, + { + "ct": 2821, + "ccm": + [ + 1.18251, 0.15501, -0.33752, + -0.44304, 1.58495, -0.14191, + -0.05077, -0.96422, 2.01498 + ] + }, + { + "ct": 2925, + "ccm": + [ + 1.18668, 0.00195, -0.18864, + -0.41617, 1.50514, -0.08897, + -0.02675, -0.91143, 1.93818 + ] + }, + { + "ct": 2926, + "ccm": + [ + 1.50948, -0.44421, -0.06527, + -0.37241, 1.41726, -0.04486, + 0.07098, -0.84694, 1.77596 + ] + }, + { + "ct": 2951, + "ccm": + [ + 1.52743, -0.47333, -0.05411, + -0.36485, 1.40764, -0.04279, + 0.08672, -0.90479, 1.81807 + ] + }, + { + "ct": 2954, + "ccm": + [ + 1.51683, -0.46841, -0.04841, + -0.36288, 1.39914, -0.03625, + 0.06421, -0.82034, 1.75613 + ] + }, + { + "ct": 3578, + "ccm": + [ + 1.59888, -0.59105, -0.00784, + -0.29366, 1.32037, -0.02671, + 0.06627, -0.76465, 1.69838 + ] + }, + { + "ct": 3717, + "ccm": + [ + 1.59063, -0.58059, -0.01003, + -0.29583, 1.32715, -0.03132, + 0.03613, -0.67431, 1.63817 + ] + }, + { + "ct": 3784, + "ccm": + [ + 1.59379, -0.58861, -0.00517, + -0.29178, 1.33292, -0.04115, + 0.03541, -0.66162, 1.62622 + ] + }, + { + "ct": 4485, + "ccm": + [ + 1.40761, -0.34561, -0.06201, + -0.32388, 1.57221, -0.24832, + -0.01014, -0.63427, 1.64441 + ] + }, + { + "ct": 4615, + "ccm": + [ + 1.41537, -0.35832, -0.05705, + -0.31429, 1.56019, -0.24591, + -0.01761, -0.61859, 1.63621 + ] + }, + { + "ct": 4671, + "ccm": + [ + 1.42941, -0.38178, -0.04764, + -0.31421, 1.55925, -0.24504, + -0.01141, -0.62987, 1.64129 + ] + }, + { + "ct": 5753, + "ccm": + [ + 1.64549, -0.63329, -0.01221, + -0.22431, 1.36423, -0.13992, + -0.00831, -0.55373, 1.56204 + ] + }, + { + "ct": 5773, + "ccm": + [ + 1.63668, -0.63557, -0.00111, + -0.21919, 1.36234, -0.14315, + -0.00399, -0.57428, 1.57827 + ] + }, + { + "ct": 7433, + "ccm": + [ + 1.36007, -0.09277, -0.26729, + -0.36886, 2.09249, -0.72363, + -0.12573, -0.76761, 1.89334 + ] + }, + { + "ct": 55792, + "ccm": + [ + 1.65091, -0.63689, -0.01401, + -0.22277, 1.35752, -0.13475, + -0.00943, -0.55091, 1.56033 + ] + } + ] + } + }, + { + "rpi.sharpen": { } + }, + { + "rpi.af": + { + "ranges": + { + "normal": + { + "min": 0.0, + "max": 12.0, + "default": 1.0 + }, + "macro": + { + "min": 3.0, + "max": 15.0, + "default": 4.0 + } + }, + "speeds": + { + "normal": + { + "step_coarse": 1.0, + "step_fine": 0.25, + "contrast_ratio": 0.75, + "pdaf_gain": -0.02, + "pdaf_squelch": 0.125, + "max_slew": 2.0, + "pdaf_frames": 20, + "dropout_frames": 6, + "step_frames": 4 + } + }, + "conf_epsilon": 8, + "conf_thresh": 16, + "conf_clip": 512, + "skip_frames": 5, + "map": [ 0.0, 445, 15.0, 925 ] + } + } + ] +} diff --git a/src/ipa/rpi/vc4/data/imx708_wide.json b/src/ipa/rpi/vc4/data/imx708_wide.json new file mode 100644 index 00000000..b772efee --- /dev/null +++ b/src/ipa/rpi/vc4/data/imx708_wide.json @@ -0,0 +1,459 @@ +{ + "version": 2.0, + "target": "bcm2835", + "algorithms": [ + { + "rpi.black_level": + { + "black_level": 4096 + } + }, + { + "rpi.dpc": { } + }, + { + "rpi.lux": + { + "reference_shutter_speed": 9989, + "reference_gain": 1.23, + "reference_aperture": 1.0, + "reference_lux": 980, + "reference_Y": 8345 + } + }, + { + "rpi.noise": + { + "reference_constant": 16.0, + "reference_slope": 4.0 + } + }, + { + "rpi.geq": + { + "offset": 215, + "slope": 0.00287 + } + }, + { + "rpi.sdn": { } + }, + { + "rpi.awb": + { + "priors": [ + { + "lux": 0, + "prior": + [ + 2000, 1.0, + 3000, 0.0, + 13000, 0.0 + ] + }, + { + "lux": 800, + "prior": + [ + 2000, 0.0, + 6000, 2.0, + 13000, 2.0 + ] + }, + { + "lux": 1500, + "prior": + [ + 2000, 0.0, + 4000, 1.0, + 6000, 6.0, + 6500, 7.0, + 7000, 1.0, + 13000, 1.0 + ] + } + ], + "modes": + { + "auto": + { + "lo": 2500, + "hi": 8000 + }, + "incandescent": + { + "lo": 2500, + "hi": 3000 + }, + "tungsten": + { + "lo": 3000, + "hi": 3500 + }, + "fluorescent": + { + "lo": 4000, + "hi": 4700 + }, + "indoor": + { + "lo": 3000, + "hi": 5000 + }, + "daylight": + { + "lo": 5500, + "hi": 6500 + }, + "cloudy": + { + "lo": 7000, + "hi": 8600 + } + }, + "bayes": 1, + "ct_curve": + [ + 2750.0, 0.7881, 0.2849, + 2940.0, 0.7559, 0.3103, + 3650.0, 0.6291, 0.4206, + 4625.0, 0.5336, 0.5161, + 5715.0, 0.4668, 0.5898 + ], + "sensitivity_r": 1.05, + "sensitivity_b": 1.05, + "transverse_pos": 0.01165, + "transverse_neg": 0.01601 + } + }, + { + "rpi.agc": + { + "metering_modes": + { + "centre-weighted": + { + "weights": [ 3, 3, 3, 2, 2, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0 ] + }, + "spot": + { + "weights": [ 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ] + }, + "matrix": + { + "weights": [ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ] + } + }, + "exposure_modes": + { + "normal": + { + "shutter": [ 100, 15000, 30000, 60000, 120000 ], + "gain": [ 1.0, 1.0, 2.0, 4.0, 6.0 ] + }, + "short": + { + "shutter": [ 100, 5000, 10000, 20000, 120000 ], + "gain": [ 1.0, 2.0, 4.0, 6.0, 6.0 ] + } + }, + "constraint_modes": + { + "normal": [ + { + "bound": "LOWER", + "q_lo": 0.98, + "q_hi": 1.0, + "y_target": + [ + 0, 0.2, + 1000, 0.2 + ] + } + ], + "highlight": [ + { + "bound": "LOWER", + "q_lo": 0.98, + "q_hi": 1.0, + "y_target": + [ + 0, 0.2, + 1000, 0.2 + ] + }, + { + "bound": "UPPER", + "q_lo": 0.98, + "q_hi": 1.0, + "y_target": + [ + 0, 0.8, + 1000, 0.8 + ] + } + ] + }, + "y_target": + [ + 0, 0.16, + 1000, 0.165, + 10000, 0.17 + ], + "startup_frames": 5, + "convergence_frames": 6, + "speed": 0.15 + } + }, + { + "rpi.alsc": + { + "omega": 1.3, + "n_iter": 100, + "luminance_strength": 0.5, + "calibrations_Cr": [ + { + "ct": 3000, + "table": + [ + 1.529, 1.526, 1.522, 1.506, 1.489, 1.473, 1.458, 1.456, 1.456, 1.458, 1.474, 1.493, 1.513, 1.531, 1.541, 1.544, + 1.527, 1.523, 1.511, 1.491, 1.474, 1.459, 1.445, 1.441, 1.441, 1.446, 1.461, 1.479, 1.499, 1.521, 1.536, 1.541, + 1.524, 1.515, 1.498, 1.477, 1.459, 1.444, 1.431, 1.426, 1.426, 1.435, 1.446, 1.466, 1.487, 1.507, 1.528, 1.538, + 1.522, 1.512, 1.491, 1.468, 1.447, 1.431, 1.423, 1.417, 1.418, 1.425, 1.435, 1.455, 1.479, 1.499, 1.523, 1.537, + 1.522, 1.509, 1.485, 1.463, 1.441, 1.423, 1.416, 1.413, 1.415, 1.418, 1.429, 1.449, 1.473, 1.495, 1.521, 1.538, + 1.522, 1.508, 1.483, 1.461, 1.438, 1.421, 1.413, 1.412, 1.412, 1.415, 1.428, 1.447, 1.471, 1.493, 1.519, 1.538, + 1.522, 1.509, 1.484, 1.462, 1.439, 1.421, 1.414, 1.411, 1.412, 1.416, 1.428, 1.447, 1.471, 1.493, 1.519, 1.537, + 1.523, 1.511, 1.487, 1.465, 1.443, 1.424, 1.417, 1.413, 1.415, 1.419, 1.429, 1.451, 1.473, 1.494, 1.519, 1.536, + 1.524, 1.514, 1.493, 1.471, 1.451, 1.434, 1.424, 1.419, 1.419, 1.428, 1.437, 1.457, 1.477, 1.498, 1.521, 1.538, + 1.527, 1.521, 1.503, 1.481, 1.462, 1.449, 1.434, 1.429, 1.429, 1.437, 1.451, 1.469, 1.488, 1.508, 1.527, 1.539, + 1.529, 1.527, 1.515, 1.495, 1.477, 1.462, 1.449, 1.444, 1.444, 1.451, 1.467, 1.481, 1.499, 1.519, 1.535, 1.543, + 1.534, 1.531, 1.527, 1.512, 1.492, 1.476, 1.463, 1.461, 1.461, 1.464, 1.479, 1.495, 1.515, 1.533, 1.543, 1.546 + ] + }, + { + "ct": 5000, + "table": + [ + 2.603, 2.599, 2.591, 2.567, 2.539, 2.515, 2.489, 2.489, 2.489, 2.491, 2.516, 2.543, 2.574, 2.597, 2.614, 2.617, + 2.596, 2.591, 2.571, 2.542, 2.516, 2.489, 2.464, 2.458, 2.458, 2.469, 2.492, 2.518, 2.547, 2.576, 2.602, 2.614, + 2.591, 2.576, 2.546, 2.519, 2.489, 2.464, 2.437, 2.427, 2.427, 2.441, 2.467, 2.492, 2.525, 2.553, 2.586, 2.605, + 2.588, 2.568, 2.534, 2.503, 2.472, 2.437, 2.423, 2.409, 2.411, 2.425, 2.441, 2.475, 2.513, 2.541, 2.577, 2.602, + 2.588, 2.565, 2.527, 2.494, 2.461, 2.425, 2.409, 2.399, 2.403, 2.409, 2.431, 2.466, 2.503, 2.534, 2.571, 2.601, + 2.586, 2.561, 2.525, 2.491, 2.454, 2.418, 2.399, 2.396, 2.395, 2.402, 2.424, 2.461, 2.501, 2.531, 2.567, 2.599, + 2.583, 2.559, 2.525, 2.491, 2.454, 2.418, 2.398, 2.393, 2.393, 2.401, 2.423, 2.459, 2.498, 2.531, 2.566, 2.597, + 2.583, 2.559, 2.526, 2.494, 2.458, 2.421, 2.404, 2.397, 2.399, 2.404, 2.426, 2.461, 2.501, 2.531, 2.566, 2.596, + 2.583, 2.563, 2.531, 2.501, 2.469, 2.435, 2.419, 2.405, 2.404, 2.422, 2.435, 2.471, 2.505, 2.537, 2.572, 2.596, + 2.585, 2.571, 2.539, 2.516, 2.486, 2.458, 2.435, 2.424, 2.424, 2.435, 2.459, 2.489, 2.521, 2.546, 2.579, 2.601, + 2.589, 2.578, 2.557, 2.532, 2.506, 2.483, 2.458, 2.449, 2.449, 2.459, 2.485, 2.507, 2.535, 2.563, 2.591, 2.605, + 2.589, 2.586, 2.575, 2.551, 2.525, 2.503, 2.481, 2.476, 2.476, 2.481, 2.504, 2.526, 2.555, 2.583, 2.604, 2.611 + ] + } + ], + "calibrations_Cb": [ + { + "ct": 3000, + "table": + [ + 3.311, 3.339, 3.369, 3.374, 3.371, 3.363, 3.356, 3.353, 3.353, 3.353, 3.357, 3.362, 3.362, 3.356, 3.328, 3.311, + 3.321, 3.354, 3.374, 3.374, 3.368, 3.359, 3.352, 3.349, 3.347, 3.347, 3.349, 3.357, 3.361, 3.359, 3.343, 3.324, + 3.334, 3.368, 3.375, 3.374, 3.365, 3.356, 3.349, 3.347, 3.346, 3.346, 3.347, 3.349, 3.358, 3.361, 3.357, 3.336, + 3.346, 3.378, 3.378, 3.369, 3.363, 3.358, 3.351, 3.348, 3.347, 3.346, 3.347, 3.348, 3.354, 3.364, 3.363, 3.345, + 3.351, 3.381, 3.381, 3.368, 3.361, 3.357, 3.349, 3.347, 3.347, 3.345, 3.345, 3.347, 3.353, 3.364, 3.364, 3.347, + 3.353, 3.379, 3.379, 3.366, 3.359, 3.351, 3.348, 3.343, 3.342, 3.342, 3.343, 3.345, 3.351, 3.363, 3.363, 3.347, + 3.353, 3.376, 3.376, 3.363, 3.351, 3.347, 3.343, 3.338, 3.336, 3.338, 3.339, 3.343, 3.351, 3.361, 3.361, 3.347, + 3.351, 3.374, 3.374, 3.359, 3.351, 3.345, 3.338, 3.334, 3.333, 3.334, 3.336, 3.339, 3.347, 3.358, 3.358, 3.345, + 3.346, 3.368, 3.368, 3.359, 3.349, 3.343, 3.336, 3.332, 3.327, 3.331, 3.333, 3.337, 3.346, 3.356, 3.356, 3.341, + 3.336, 3.362, 3.364, 3.359, 3.351, 3.342, 3.334, 3.324, 3.324, 3.325, 3.329, 3.336, 3.346, 3.351, 3.351, 3.333, + 3.324, 3.349, 3.359, 3.358, 3.352, 3.341, 3.329, 3.323, 3.321, 3.322, 3.326, 3.336, 3.346, 3.347, 3.339, 3.319, + 3.311, 3.328, 3.352, 3.354, 3.352, 3.341, 3.329, 3.321, 3.319, 3.321, 3.324, 3.338, 3.343, 3.343, 3.319, 3.312 + ] + }, + { + "ct": 5000, + "table": + [ + 1.634, 1.647, 1.665, 1.668, 1.668, 1.664, 1.662, 1.662, 1.661, 1.661, 1.661, 1.663, 1.663, 1.659, 1.643, 1.636, + 1.639, 1.656, 1.668, 1.669, 1.668, 1.666, 1.664, 1.663, 1.663, 1.661, 1.661, 1.662, 1.663, 1.662, 1.654, 1.642, + 1.645, 1.663, 1.669, 1.668, 1.667, 1.667, 1.667, 1.668, 1.668, 1.665, 1.662, 1.661, 1.662, 1.664, 1.661, 1.649, + 1.651, 1.669, 1.669, 1.667, 1.666, 1.668, 1.669, 1.672, 1.672, 1.668, 1.665, 1.661, 1.661, 1.665, 1.665, 1.655, + 1.654, 1.669, 1.669, 1.666, 1.666, 1.669, 1.672, 1.673, 1.673, 1.671, 1.666, 1.661, 1.661, 1.665, 1.665, 1.659, + 1.654, 1.669, 1.669, 1.666, 1.666, 1.669, 1.671, 1.673, 1.672, 1.669, 1.667, 1.661, 1.661, 1.665, 1.665, 1.659, + 1.654, 1.668, 1.668, 1.664, 1.663, 1.667, 1.669, 1.671, 1.669, 1.668, 1.665, 1.661, 1.661, 1.663, 1.663, 1.659, + 1.653, 1.665, 1.665, 1.661, 1.661, 1.664, 1.667, 1.668, 1.668, 1.665, 1.661, 1.658, 1.659, 1.662, 1.662, 1.657, + 1.651, 1.664, 1.664, 1.659, 1.659, 1.661, 1.663, 1.663, 1.662, 1.661, 1.658, 1.656, 1.657, 1.662, 1.662, 1.655, + 1.645, 1.661, 1.663, 1.661, 1.659, 1.659, 1.659, 1.657, 1.657, 1.656, 1.654, 1.655, 1.656, 1.661, 1.661, 1.649, + 1.641, 1.654, 1.661, 1.661, 1.659, 1.657, 1.655, 1.653, 1.652, 1.651, 1.652, 1.653, 1.657, 1.658, 1.655, 1.644, + 1.635, 1.645, 1.661, 1.661, 1.661, 1.655, 1.653, 1.649, 1.648, 1.647, 1.651, 1.653, 1.657, 1.657, 1.646, 1.638 + ] + } + ], + "luminance_lut": + [ + 3.535, 3.279, 3.049, 2.722, 2.305, 1.958, 1.657, 1.647, 1.647, 1.656, 1.953, 2.289, 2.707, 3.058, 3.325, 3.589, + 3.379, 3.157, 2.874, 2.421, 1.973, 1.735, 1.472, 1.388, 1.388, 1.471, 1.724, 1.963, 2.409, 2.877, 3.185, 3.416, + 3.288, 3.075, 2.696, 2.169, 1.735, 1.472, 1.311, 1.208, 1.208, 1.306, 1.471, 1.724, 2.159, 2.695, 3.092, 3.321, + 3.238, 3.001, 2.534, 1.981, 1.572, 1.311, 1.207, 1.082, 1.082, 1.204, 1.306, 1.563, 1.973, 2.529, 3.008, 3.259, + 3.211, 2.938, 2.414, 1.859, 1.468, 1.221, 1.082, 1.036, 1.031, 1.079, 1.217, 1.463, 1.851, 2.403, 2.931, 3.229, + 3.206, 2.904, 2.356, 1.802, 1.421, 1.181, 1.037, 1.002, 1.002, 1.032, 1.175, 1.414, 1.793, 2.343, 2.899, 3.223, + 3.206, 2.904, 2.356, 1.802, 1.421, 1.181, 1.037, 1.005, 1.005, 1.032, 1.175, 1.414, 1.793, 2.343, 2.899, 3.223, + 3.211, 2.936, 2.417, 1.858, 1.468, 1.222, 1.083, 1.037, 1.032, 1.083, 1.218, 1.463, 1.848, 2.403, 2.932, 3.226, + 3.234, 2.997, 2.536, 1.979, 1.569, 1.311, 1.206, 1.084, 1.084, 1.204, 1.305, 1.565, 1.966, 2.524, 2.996, 3.251, + 3.282, 3.069, 2.697, 2.166, 1.731, 1.471, 1.311, 1.207, 1.207, 1.305, 1.466, 1.729, 2.158, 2.689, 3.077, 3.304, + 3.369, 3.146, 2.873, 2.415, 1.964, 1.722, 1.471, 1.382, 1.382, 1.466, 1.722, 1.964, 2.408, 2.871, 3.167, 3.401, + 3.524, 3.253, 3.025, 2.691, 2.275, 1.939, 1.657, 1.628, 1.628, 1.654, 1.936, 2.275, 2.687, 3.029, 3.284, 3.574 + ], + "sigma": 0.00195, + "sigma_Cb": 0.00241 + } + }, + { + "rpi.contrast": + { + "ce_enable": 1, + "gamma_curve": + [ + 0, 0, + 1024, 5040, + 2048, 9338, + 3072, 12356, + 4096, 15312, + 5120, 18051, + 6144, 20790, + 7168, 23193, + 8192, 25744, + 9216, 27942, + 10240, 30035, + 11264, 32005, + 12288, 33975, + 13312, 35815, + 14336, 37600, + 15360, 39168, + 16384, 40642, + 18432, 43379, + 20480, 45749, + 22528, 47753, + 24576, 49621, + 26624, 51253, + 28672, 52698, + 30720, 53796, + 32768, 54876, + 36864, 57012, + 40960, 58656, + 45056, 59954, + 49152, 61183, + 53248, 62355, + 57344, 63419, + 61440, 64476, + 65535, 65535 + ] + } + }, + { + "rpi.ccm": + { + "ccms": [ + { + "ct": 2750, + "ccm": + [ + 1.13004, 0.36392, -0.49396, + -0.45885, 1.68171, -0.22286, + -0.06473, -0.86962, 1.93435 + ] + }, + { + "ct": 2940, + "ccm": + [ + 1.29876, 0.09627, -0.39503, + -0.43085, 1.60258, -0.17172, + -0.02638, -0.92581, 1.95218 + ] + }, + { + "ct": 3650, + "ccm": + [ + 1.57729, -0.29734, -0.27995, + -0.42965, 1.66231, -0.23265, + -0.02183, -0.62331, 1.64514 + ] + }, + { + "ct": 4625, + "ccm": + [ + 1.52145, -0.22382, -0.29763, + -0.40445, 1.82186, -0.41742, + -0.05732, -0.56222, 1.61954 + ] + }, + { + "ct": 5715, + "ccm": + [ + 1.67851, -0.39193, -0.28658, + -0.37169, 1.72949, -0.35781, + -0.09556, -0.41951, 1.51508 + ] + } + ] + } + }, + { + "rpi.sharpen": { } + }, + { + "rpi.af": + { + "ranges": + { + "normal": + { + "min": 0.0, + "max": 12.0, + "default": 1.0 + }, + "macro": + { + "min": 4.0, + "max": 32.0, + "default": 6.0 + } + }, + "speeds": + { + "normal": + { + "step_coarse": 2.0, + "step_fine": 0.5, + "contrast_ratio": 0.75, + "pdaf_gain": -0.03, + "pdaf_squelch": 0.2, + "max_slew": 4.0, + "pdaf_frames": 20, + "dropout_frames": 6, + "step_frames": 4 + }, + "fast": + { + "step_coarse": 2.0, + "step_fine": 0.5, + "contrast_ratio": 0.75, + "pdaf_gain": -0.05, + "pdaf_squelch": 0.2, + "max_slew": 5.0, + "pdaf_frames": 16, + "dropout_frames": 6, + "step_frames": 4 + } + }, + "conf_epsilon": 8, + "conf_thresh": 12, + "conf_clip": 512, + "skip_frames": 5, + "map": [ 0.0, 420, 35.0, 920 ] + } + } + ] +} diff --git a/src/ipa/rpi/vc4/data/imx708_wide_noir.json b/src/ipa/rpi/vc4/data/imx708_wide_noir.json new file mode 100644 index 00000000..c5f6b53d --- /dev/null +++ b/src/ipa/rpi/vc4/data/imx708_wide_noir.json @@ -0,0 +1,459 @@ +{ + "version": 2.0, + "target": "bcm2835", + "algorithms": [ + { + "rpi.black_level": + { + "black_level": 4096 + } + }, + { + "rpi.dpc": { } + }, + { + "rpi.lux": + { + "reference_shutter_speed": 9989, + "reference_gain": 1.23, + "reference_aperture": 1.0, + "reference_lux": 980, + "reference_Y": 8345 + } + }, + { + "rpi.noise": + { + "reference_constant": 16.0, + "reference_slope": 4.0 + } + }, + { + "rpi.geq": + { + "offset": 215, + "slope": 0.00287 + } + }, + { + "rpi.sdn": { } + }, + { + "rpi.awb": + { + "priors": [ + { + "lux": 0, + "prior": + [ + 2000, 1.0, + 3000, 0.0, + 13000, 0.0 + ] + }, + { + "lux": 800, + "prior": + [ + 2000, 0.0, + 6000, 2.0, + 13000, 2.0 + ] + }, + { + "lux": 1500, + "prior": + [ + 2000, 0.0, + 4000, 1.0, + 6000, 6.0, + 6500, 7.0, + 7000, 1.0, + 13000, 1.0 + ] + } + ], + "modes": + { + "auto": + { + "lo": 2500, + "hi": 8000 + }, + "incandescent": + { + "lo": 2500, + "hi": 3000 + }, + "tungsten": + { + "lo": 3000, + "hi": 3500 + }, + "fluorescent": + { + "lo": 4000, + "hi": 4700 + }, + "indoor": + { + "lo": 3000, + "hi": 5000 + }, + "daylight": + { + "lo": 5500, + "hi": 6500 + }, + "cloudy": + { + "lo": 7000, + "hi": 8600 + } + }, + "bayes": 0, + "ct_curve": + [ + 2750.0, 0.7881, 0.2849, + 2940.0, 0.7559, 0.3103, + 3650.0, 0.6291, 0.4206, + 4625.0, 0.5336, 0.5161, + 5715.0, 0.4668, 0.5898 + ], + "sensitivity_r": 1.05, + "sensitivity_b": 1.05, + "transverse_pos": 0.01165, + "transverse_neg": 0.01601 + } + }, + { + "rpi.agc": + { + "metering_modes": + { + "centre-weighted": + { + "weights": [ 3, 3, 3, 2, 2, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0 ] + }, + "spot": + { + "weights": [ 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ] + }, + "matrix": + { + "weights": [ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ] + } + }, + "exposure_modes": + { + "normal": + { + "shutter": [ 100, 15000, 30000, 60000, 120000 ], + "gain": [ 1.0, 1.0, 2.0, 4.0, 6.0 ] + }, + "short": + { + "shutter": [ 100, 5000, 10000, 20000, 120000 ], + "gain": [ 1.0, 2.0, 4.0, 6.0, 6.0 ] + } + }, + "constraint_modes": + { + "normal": [ + { + "bound": "LOWER", + "q_lo": 0.98, + "q_hi": 1.0, + "y_target": + [ + 0, 0.2, + 1000, 0.2 + ] + } + ], + "highlight": [ + { + "bound": "LOWER", + "q_lo": 0.98, + "q_hi": 1.0, + "y_target": + [ + 0, 0.2, + 1000, 0.2 + ] + }, + { + "bound": "UPPER", + "q_lo": 0.98, + "q_hi": 1.0, + "y_target": + [ + 0, 0.8, + 1000, 0.8 + ] + } + ] + }, + "y_target": + [ + 0, 0.16, + 1000, 0.165, + 10000, 0.17 + ], + "startup_frames": 5, + "convergence_frames": 6, + "speed": 0.15 + } + }, + { + "rpi.alsc": + { + "omega": 1.3, + "n_iter": 100, + "luminance_strength": 0.5, + "calibrations_Cr": [ + { + "ct": 3000, + "table": + [ + 1.529, 1.526, 1.522, 1.506, 1.489, 1.473, 1.458, 1.456, 1.456, 1.458, 1.474, 1.493, 1.513, 1.531, 1.541, 1.544, + 1.527, 1.523, 1.511, 1.491, 1.474, 1.459, 1.445, 1.441, 1.441, 1.446, 1.461, 1.479, 1.499, 1.521, 1.536, 1.541, + 1.524, 1.515, 1.498, 1.477, 1.459, 1.444, 1.431, 1.426, 1.426, 1.435, 1.446, 1.466, 1.487, 1.507, 1.528, 1.538, + 1.522, 1.512, 1.491, 1.468, 1.447, 1.431, 1.423, 1.417, 1.418, 1.425, 1.435, 1.455, 1.479, 1.499, 1.523, 1.537, + 1.522, 1.509, 1.485, 1.463, 1.441, 1.423, 1.416, 1.413, 1.415, 1.418, 1.429, 1.449, 1.473, 1.495, 1.521, 1.538, + 1.522, 1.508, 1.483, 1.461, 1.438, 1.421, 1.413, 1.412, 1.412, 1.415, 1.428, 1.447, 1.471, 1.493, 1.519, 1.538, + 1.522, 1.509, 1.484, 1.462, 1.439, 1.421, 1.414, 1.411, 1.412, 1.416, 1.428, 1.447, 1.471, 1.493, 1.519, 1.537, + 1.523, 1.511, 1.487, 1.465, 1.443, 1.424, 1.417, 1.413, 1.415, 1.419, 1.429, 1.451, 1.473, 1.494, 1.519, 1.536, + 1.524, 1.514, 1.493, 1.471, 1.451, 1.434, 1.424, 1.419, 1.419, 1.428, 1.437, 1.457, 1.477, 1.498, 1.521, 1.538, + 1.527, 1.521, 1.503, 1.481, 1.462, 1.449, 1.434, 1.429, 1.429, 1.437, 1.451, 1.469, 1.488, 1.508, 1.527, 1.539, + 1.529, 1.527, 1.515, 1.495, 1.477, 1.462, 1.449, 1.444, 1.444, 1.451, 1.467, 1.481, 1.499, 1.519, 1.535, 1.543, + 1.534, 1.531, 1.527, 1.512, 1.492, 1.476, 1.463, 1.461, 1.461, 1.464, 1.479, 1.495, 1.515, 1.533, 1.543, 1.546 + ] + }, + { + "ct": 5000, + "table": + [ + 2.603, 2.599, 2.591, 2.567, 2.539, 2.515, 2.489, 2.489, 2.489, 2.491, 2.516, 2.543, 2.574, 2.597, 2.614, 2.617, + 2.596, 2.591, 2.571, 2.542, 2.516, 2.489, 2.464, 2.458, 2.458, 2.469, 2.492, 2.518, 2.547, 2.576, 2.602, 2.614, + 2.591, 2.576, 2.546, 2.519, 2.489, 2.464, 2.437, 2.427, 2.427, 2.441, 2.467, 2.492, 2.525, 2.553, 2.586, 2.605, + 2.588, 2.568, 2.534, 2.503, 2.472, 2.437, 2.423, 2.409, 2.411, 2.425, 2.441, 2.475, 2.513, 2.541, 2.577, 2.602, + 2.588, 2.565, 2.527, 2.494, 2.461, 2.425, 2.409, 2.399, 2.403, 2.409, 2.431, 2.466, 2.503, 2.534, 2.571, 2.601, + 2.586, 2.561, 2.525, 2.491, 2.454, 2.418, 2.399, 2.396, 2.395, 2.402, 2.424, 2.461, 2.501, 2.531, 2.567, 2.599, + 2.583, 2.559, 2.525, 2.491, 2.454, 2.418, 2.398, 2.393, 2.393, 2.401, 2.423, 2.459, 2.498, 2.531, 2.566, 2.597, + 2.583, 2.559, 2.526, 2.494, 2.458, 2.421, 2.404, 2.397, 2.399, 2.404, 2.426, 2.461, 2.501, 2.531, 2.566, 2.596, + 2.583, 2.563, 2.531, 2.501, 2.469, 2.435, 2.419, 2.405, 2.404, 2.422, 2.435, 2.471, 2.505, 2.537, 2.572, 2.596, + 2.585, 2.571, 2.539, 2.516, 2.486, 2.458, 2.435, 2.424, 2.424, 2.435, 2.459, 2.489, 2.521, 2.546, 2.579, 2.601, + 2.589, 2.578, 2.557, 2.532, 2.506, 2.483, 2.458, 2.449, 2.449, 2.459, 2.485, 2.507, 2.535, 2.563, 2.591, 2.605, + 2.589, 2.586, 2.575, 2.551, 2.525, 2.503, 2.481, 2.476, 2.476, 2.481, 2.504, 2.526, 2.555, 2.583, 2.604, 2.611 + ] + } + ], + "calibrations_Cb": [ + { + "ct": 3000, + "table": + [ + 3.311, 3.339, 3.369, 3.374, 3.371, 3.363, 3.356, 3.353, 3.353, 3.353, 3.357, 3.362, 3.362, 3.356, 3.328, 3.311, + 3.321, 3.354, 3.374, 3.374, 3.368, 3.359, 3.352, 3.349, 3.347, 3.347, 3.349, 3.357, 3.361, 3.359, 3.343, 3.324, + 3.334, 3.368, 3.375, 3.374, 3.365, 3.356, 3.349, 3.347, 3.346, 3.346, 3.347, 3.349, 3.358, 3.361, 3.357, 3.336, + 3.346, 3.378, 3.378, 3.369, 3.363, 3.358, 3.351, 3.348, 3.347, 3.346, 3.347, 3.348, 3.354, 3.364, 3.363, 3.345, + 3.351, 3.381, 3.381, 3.368, 3.361, 3.357, 3.349, 3.347, 3.347, 3.345, 3.345, 3.347, 3.353, 3.364, 3.364, 3.347, + 3.353, 3.379, 3.379, 3.366, 3.359, 3.351, 3.348, 3.343, 3.342, 3.342, 3.343, 3.345, 3.351, 3.363, 3.363, 3.347, + 3.353, 3.376, 3.376, 3.363, 3.351, 3.347, 3.343, 3.338, 3.336, 3.338, 3.339, 3.343, 3.351, 3.361, 3.361, 3.347, + 3.351, 3.374, 3.374, 3.359, 3.351, 3.345, 3.338, 3.334, 3.333, 3.334, 3.336, 3.339, 3.347, 3.358, 3.358, 3.345, + 3.346, 3.368, 3.368, 3.359, 3.349, 3.343, 3.336, 3.332, 3.327, 3.331, 3.333, 3.337, 3.346, 3.356, 3.356, 3.341, + 3.336, 3.362, 3.364, 3.359, 3.351, 3.342, 3.334, 3.324, 3.324, 3.325, 3.329, 3.336, 3.346, 3.351, 3.351, 3.333, + 3.324, 3.349, 3.359, 3.358, 3.352, 3.341, 3.329, 3.323, 3.321, 3.322, 3.326, 3.336, 3.346, 3.347, 3.339, 3.319, + 3.311, 3.328, 3.352, 3.354, 3.352, 3.341, 3.329, 3.321, 3.319, 3.321, 3.324, 3.338, 3.343, 3.343, 3.319, 3.312 + ] + }, + { + "ct": 5000, + "table": + [ + 1.634, 1.647, 1.665, 1.668, 1.668, 1.664, 1.662, 1.662, 1.661, 1.661, 1.661, 1.663, 1.663, 1.659, 1.643, 1.636, + 1.639, 1.656, 1.668, 1.669, 1.668, 1.666, 1.664, 1.663, 1.663, 1.661, 1.661, 1.662, 1.663, 1.662, 1.654, 1.642, + 1.645, 1.663, 1.669, 1.668, 1.667, 1.667, 1.667, 1.668, 1.668, 1.665, 1.662, 1.661, 1.662, 1.664, 1.661, 1.649, + 1.651, 1.669, 1.669, 1.667, 1.666, 1.668, 1.669, 1.672, 1.672, 1.668, 1.665, 1.661, 1.661, 1.665, 1.665, 1.655, + 1.654, 1.669, 1.669, 1.666, 1.666, 1.669, 1.672, 1.673, 1.673, 1.671, 1.666, 1.661, 1.661, 1.665, 1.665, 1.659, + 1.654, 1.669, 1.669, 1.666, 1.666, 1.669, 1.671, 1.673, 1.672, 1.669, 1.667, 1.661, 1.661, 1.665, 1.665, 1.659, + 1.654, 1.668, 1.668, 1.664, 1.663, 1.667, 1.669, 1.671, 1.669, 1.668, 1.665, 1.661, 1.661, 1.663, 1.663, 1.659, + 1.653, 1.665, 1.665, 1.661, 1.661, 1.664, 1.667, 1.668, 1.668, 1.665, 1.661, 1.658, 1.659, 1.662, 1.662, 1.657, + 1.651, 1.664, 1.664, 1.659, 1.659, 1.661, 1.663, 1.663, 1.662, 1.661, 1.658, 1.656, 1.657, 1.662, 1.662, 1.655, + 1.645, 1.661, 1.663, 1.661, 1.659, 1.659, 1.659, 1.657, 1.657, 1.656, 1.654, 1.655, 1.656, 1.661, 1.661, 1.649, + 1.641, 1.654, 1.661, 1.661, 1.659, 1.657, 1.655, 1.653, 1.652, 1.651, 1.652, 1.653, 1.657, 1.658, 1.655, 1.644, + 1.635, 1.645, 1.661, 1.661, 1.661, 1.655, 1.653, 1.649, 1.648, 1.647, 1.651, 1.653, 1.657, 1.657, 1.646, 1.638 + ] + } + ], + "luminance_lut": + [ + 3.535, 3.279, 3.049, 2.722, 2.305, 1.958, 1.657, 1.647, 1.647, 1.656, 1.953, 2.289, 2.707, 3.058, 3.325, 3.589, + 3.379, 3.157, 2.874, 2.421, 1.973, 1.735, 1.472, 1.388, 1.388, 1.471, 1.724, 1.963, 2.409, 2.877, 3.185, 3.416, + 3.288, 3.075, 2.696, 2.169, 1.735, 1.472, 1.311, 1.208, 1.208, 1.306, 1.471, 1.724, 2.159, 2.695, 3.092, 3.321, + 3.238, 3.001, 2.534, 1.981, 1.572, 1.311, 1.207, 1.082, 1.082, 1.204, 1.306, 1.563, 1.973, 2.529, 3.008, 3.259, + 3.211, 2.938, 2.414, 1.859, 1.468, 1.221, 1.082, 1.036, 1.031, 1.079, 1.217, 1.463, 1.851, 2.403, 2.931, 3.229, + 3.206, 2.904, 2.356, 1.802, 1.421, 1.181, 1.037, 1.002, 1.002, 1.032, 1.175, 1.414, 1.793, 2.343, 2.899, 3.223, + 3.206, 2.904, 2.356, 1.802, 1.421, 1.181, 1.037, 1.005, 1.005, 1.032, 1.175, 1.414, 1.793, 2.343, 2.899, 3.223, + 3.211, 2.936, 2.417, 1.858, 1.468, 1.222, 1.083, 1.037, 1.032, 1.083, 1.218, 1.463, 1.848, 2.403, 2.932, 3.226, + 3.234, 2.997, 2.536, 1.979, 1.569, 1.311, 1.206, 1.084, 1.084, 1.204, 1.305, 1.565, 1.966, 2.524, 2.996, 3.251, + 3.282, 3.069, 2.697, 2.166, 1.731, 1.471, 1.311, 1.207, 1.207, 1.305, 1.466, 1.729, 2.158, 2.689, 3.077, 3.304, + 3.369, 3.146, 2.873, 2.415, 1.964, 1.722, 1.471, 1.382, 1.382, 1.466, 1.722, 1.964, 2.408, 2.871, 3.167, 3.401, + 3.524, 3.253, 3.025, 2.691, 2.275, 1.939, 1.657, 1.628, 1.628, 1.654, 1.936, 2.275, 2.687, 3.029, 3.284, 3.574 + ], + "sigma": 0.00195, + "sigma_Cb": 0.00241 + } + }, + { + "rpi.contrast": + { + "ce_enable": 1, + "gamma_curve": + [ + 0, 0, + 1024, 5040, + 2048, 9338, + 3072, 12356, + 4096, 15312, + 5120, 18051, + 6144, 20790, + 7168, 23193, + 8192, 25744, + 9216, 27942, + 10240, 30035, + 11264, 32005, + 12288, 33975, + 13312, 35815, + 14336, 37600, + 15360, 39168, + 16384, 40642, + 18432, 43379, + 20480, 45749, + 22528, 47753, + 24576, 49621, + 26624, 51253, + 28672, 52698, + 30720, 53796, + 32768, 54876, + 36864, 57012, + 40960, 58656, + 45056, 59954, + 49152, 61183, + 53248, 62355, + 57344, 63419, + 61440, 64476, + 65535, 65535 + ] + } + }, + { + "rpi.ccm": + { + "ccms": [ + { + "ct": 2750, + "ccm": + [ + 1.13004, 0.36392, -0.49396, + -0.45885, 1.68171, -0.22286, + -0.06473, -0.86962, 1.93435 + ] + }, + { + "ct": 2940, + "ccm": + [ + 1.29876, 0.09627, -0.39503, + -0.43085, 1.60258, -0.17172, + -0.02638, -0.92581, 1.95218 + ] + }, + { + "ct": 3650, + "ccm": + [ + 1.57729, -0.29734, -0.27995, + -0.42965, 1.66231, -0.23265, + -0.02183, -0.62331, 1.64514 + ] + }, + { + "ct": 4625, + "ccm": + [ + 1.52145, -0.22382, -0.29763, + -0.40445, 1.82186, -0.41742, + -0.05732, -0.56222, 1.61954 + ] + }, + { + "ct": 5715, + "ccm": + [ + 1.67851, -0.39193, -0.28658, + -0.37169, 1.72949, -0.35781, + -0.09556, -0.41951, 1.51508 + ] + } + ] + } + }, + { + "rpi.sharpen": { } + }, + { + "rpi.af": + { + "ranges": + { + "normal": + { + "min": 0.0, + "max": 12.0, + "default": 1.0 + }, + "macro": + { + "min": 4.0, + "max": 32.0, + "default": 6.0 + } + }, + "speeds": + { + "normal": + { + "step_coarse": 2.0, + "step_fine": 0.5, + "contrast_ratio": 0.75, + "pdaf_gain": -0.03, + "pdaf_squelch": 0.2, + "max_slew": 4.0, + "pdaf_frames": 20, + "dropout_frames": 6, + "step_frames": 4 + }, + "fast": + { + "step_coarse": 2.0, + "step_fine": 0.5, + "contrast_ratio": 0.75, + "pdaf_gain": -0.05, + "pdaf_squelch": 0.2, + "max_slew": 5.0, + "pdaf_frames": 16, + "dropout_frames": 6, + "step_frames": 4 + } + }, + "conf_epsilon": 8, + "conf_thresh": 12, + "conf_clip": 512, + "skip_frames": 5, + "map": [ 0.0, 420, 35.0, 920 ] + } + } + ] +} diff --git a/src/ipa/rpi/vc4/data/meson.build b/src/ipa/rpi/vc4/data/meson.build new file mode 100644 index 00000000..bcf5658b --- /dev/null +++ b/src/ipa/rpi/vc4/data/meson.build @@ -0,0 +1,26 @@ +# SPDX-License-Identifier: CC0-1.0 + +conf_files = files([ + 'imx219.json', + 'imx219_noir.json', + 'imx290.json', + 'imx296.json', + 'imx296_mono.json', + 'imx378.json', + 'imx477.json', + 'imx477_noir.json', + 'imx477_scientific.json', + 'imx519.json', + 'imx708.json', + 'imx708_noir.json', + 'imx708_wide.json', + 'imx708_wide_noir.json', + 'ov5647.json', + 'ov5647_noir.json', + 'ov9281_mono.json', + 'se327m12.json', + 'uncalibrated.json', +]) + +install_data(conf_files, + install_dir : ipa_data_dir / 'rpi' / 'vc4') diff --git a/src/ipa/rpi/vc4/data/ov5647.json b/src/ipa/rpi/vc4/data/ov5647.json new file mode 100644 index 00000000..d770e44f --- /dev/null +++ b/src/ipa/rpi/vc4/data/ov5647.json @@ -0,0 +1,487 @@ +{ + "version": 2.0, + "target": "bcm2835", + "algorithms": [ + { + "rpi.black_level": + { + "black_level": 1024 + } + }, + { + "rpi.dpc": { } + }, + { + "rpi.lux": + { + "reference_shutter_speed": 21663, + "reference_gain": 1.0, + "reference_aperture": 1.0, + "reference_lux": 987, + "reference_Y": 8961 + } + }, + { + "rpi.noise": + { + "reference_constant": 0, + "reference_slope": 4.25 + } + }, + { + "rpi.geq": + { + "offset": 401, + "slope": 0.05619 + } + }, + { + "rpi.sdn": { } + }, + { + "rpi.awb": + { + "priors": [ + { + "lux": 0, + "prior": + [ + 2000, 1.0, + 3000, 0.0, + 13000, 0.0 + ] + }, + { + "lux": 800, + "prior": + [ + 2000, 0.0, + 6000, 2.0, + 13000, 2.0 + ] + }, + { + "lux": 1500, + "prior": + [ + 2000, 0.0, + 4000, 1.0, + 6000, 6.0, + 6500, 7.0, + 7000, 1.0, + 13000, 1.0 + ] + } + ], + "modes": + { + "auto": + { + "lo": 2500, + "hi": 8000 + }, + "incandescent": + { + "lo": 2500, + "hi": 3000 + }, + "tungsten": + { + "lo": 3000, + "hi": 3500 + }, + "fluorescent": + { + "lo": 4000, + "hi": 4700 + }, + "indoor": + { + "lo": 3000, + "hi": 5000 + }, + "daylight": + { + "lo": 5500, + "hi": 6500 + }, + "cloudy": + { + "lo": 7000, + "hi": 8600 + } + }, + "bayes": 1, + "ct_curve": + [ + 2500.0, 1.0289, 0.4503, + 2803.0, 0.9428, 0.5108, + 2914.0, 0.9406, 0.5127, + 3605.0, 0.8261, 0.6249, + 4540.0, 0.7331, 0.7533, + 5699.0, 0.6715, 0.8627, + 8625.0, 0.6081, 1.0012 + ], + "sensitivity_r": 1.05, + "sensitivity_b": 1.05, + "transverse_pos": 0.0321, + "transverse_neg": 0.04313 + } + }, + { + "rpi.agc": + { + "metering_modes": + { + "centre-weighted": + { + "weights": [ 3, 3, 3, 2, 2, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0 ] + }, + "spot": + { + "weights": [ 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ] + }, + "matrix": + { + "weights": [ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ] + } + }, + "exposure_modes": + { + "normal": + { + "shutter": [ 100, 10000, 30000, 60000, 66666 ], + "gain": [ 1.0, 2.0, 4.0, 6.0, 8.0 ] + }, + "short": + { + "shutter": [ 100, 5000, 10000, 20000, 33333 ], + "gain": [ 1.0, 2.0, 4.0, 6.0, 8.0 ] + }, + "long": + { + "shutter": [ 100, 10000, 30000, 60000, 120000 ], + "gain": [ 1.0, 2.0, 4.0, 6.0, 12.0 ] + } + }, + "constraint_modes": + { + "normal": [ + { + "bound": "LOWER", + "q_lo": 0.98, + "q_hi": 1.0, + "y_target": + [ + 0, 0.5, + 1000, 0.5 + ] + } + ], + "highlight": [ + { + "bound": "LOWER", + "q_lo": 0.98, + "q_hi": 1.0, + "y_target": + [ + 0, 0.5, + 1000, 0.5 + ] + }, + { + "bound": "UPPER", + "q_lo": 0.98, + "q_hi": 1.0, + "y_target": + [ + 0, 0.8, + 1000, 0.8 + ] + } + ], + "shadows": [ + { + "bound": "LOWER", + "q_lo": 0.0, + "q_hi": 0.5, + "y_target": + [ + 0, 0.17, + 1000, 0.17 + ] + } + ] + }, + "y_target": + [ + 0, 0.16, + 1000, 0.165, + 10000, 0.17 + ], + "base_ev": 1.25 + } + }, + { + "rpi.alsc": + { + "omega": 1.3, + "n_iter": 100, + "luminance_strength": 0.5, + "calibrations_Cr": [ + { + "ct": 3000, + "table": + [ + 1.105, 1.103, 1.093, 1.083, 1.071, 1.065, 1.065, 1.065, 1.066, 1.069, 1.072, 1.077, 1.084, 1.089, 1.093, 1.093, + 1.103, 1.096, 1.084, 1.072, 1.059, 1.051, 1.047, 1.047, 1.051, 1.053, 1.059, 1.067, 1.075, 1.082, 1.085, 1.086, + 1.096, 1.084, 1.072, 1.059, 1.051, 1.045, 1.039, 1.038, 1.039, 1.045, 1.049, 1.057, 1.063, 1.072, 1.081, 1.082, + 1.092, 1.075, 1.061, 1.052, 1.045, 1.039, 1.036, 1.035, 1.035, 1.039, 1.044, 1.049, 1.056, 1.063, 1.072, 1.081, + 1.092, 1.073, 1.058, 1.048, 1.043, 1.038, 1.035, 1.033, 1.033, 1.035, 1.039, 1.044, 1.051, 1.057, 1.069, 1.078, + 1.091, 1.068, 1.054, 1.045, 1.041, 1.038, 1.035, 1.032, 1.032, 1.032, 1.036, 1.041, 1.045, 1.055, 1.069, 1.078, + 1.091, 1.068, 1.052, 1.043, 1.041, 1.038, 1.035, 1.032, 1.031, 1.032, 1.034, 1.036, 1.043, 1.055, 1.069, 1.078, + 1.092, 1.068, 1.052, 1.047, 1.042, 1.041, 1.038, 1.035, 1.032, 1.032, 1.035, 1.039, 1.043, 1.055, 1.071, 1.079, + 1.092, 1.073, 1.057, 1.051, 1.047, 1.047, 1.044, 1.041, 1.038, 1.038, 1.039, 1.043, 1.051, 1.059, 1.076, 1.083, + 1.092, 1.081, 1.068, 1.058, 1.056, 1.056, 1.053, 1.052, 1.049, 1.048, 1.048, 1.051, 1.059, 1.066, 1.083, 1.085, + 1.091, 1.087, 1.081, 1.068, 1.065, 1.064, 1.062, 1.062, 1.061, 1.056, 1.056, 1.056, 1.064, 1.069, 1.084, 1.089, + 1.091, 1.089, 1.085, 1.079, 1.069, 1.068, 1.067, 1.067, 1.067, 1.063, 1.061, 1.063, 1.068, 1.069, 1.081, 1.092 + ] + }, + { + "ct": 5000, + "table": + [ + 1.486, 1.484, 1.468, 1.449, 1.427, 1.403, 1.399, 1.399, 1.399, 1.404, 1.413, 1.433, 1.454, 1.473, 1.482, 1.488, + 1.484, 1.472, 1.454, 1.431, 1.405, 1.381, 1.365, 1.365, 1.367, 1.373, 1.392, 1.411, 1.438, 1.458, 1.476, 1.481, + 1.476, 1.458, 1.433, 1.405, 1.381, 1.361, 1.339, 1.334, 1.334, 1.346, 1.362, 1.391, 1.411, 1.438, 1.462, 1.474, + 1.471, 1.443, 1.417, 1.388, 1.361, 1.339, 1.321, 1.313, 1.313, 1.327, 1.346, 1.362, 1.391, 1.422, 1.453, 1.473, + 1.469, 1.439, 1.408, 1.377, 1.349, 1.321, 1.312, 1.299, 1.299, 1.311, 1.327, 1.348, 1.378, 1.415, 1.446, 1.468, + 1.468, 1.434, 1.402, 1.371, 1.341, 1.316, 1.299, 1.296, 1.295, 1.299, 1.314, 1.338, 1.371, 1.408, 1.441, 1.466, + 1.468, 1.434, 1.401, 1.371, 1.341, 1.316, 1.301, 1.296, 1.295, 1.297, 1.314, 1.338, 1.369, 1.408, 1.441, 1.465, + 1.469, 1.436, 1.401, 1.374, 1.348, 1.332, 1.315, 1.301, 1.301, 1.313, 1.324, 1.342, 1.372, 1.409, 1.442, 1.465, + 1.471, 1.444, 1.413, 1.388, 1.371, 1.348, 1.332, 1.323, 1.323, 1.324, 1.342, 1.362, 1.386, 1.418, 1.449, 1.467, + 1.473, 1.454, 1.431, 1.407, 1.388, 1.371, 1.359, 1.352, 1.351, 1.351, 1.362, 1.383, 1.404, 1.433, 1.462, 1.472, + 1.474, 1.461, 1.447, 1.424, 1.407, 1.394, 1.385, 1.381, 1.379, 1.381, 1.383, 1.401, 1.419, 1.444, 1.466, 1.481, + 1.474, 1.464, 1.455, 1.442, 1.421, 1.408, 1.403, 1.403, 1.403, 1.399, 1.402, 1.415, 1.432, 1.446, 1.467, 1.483 + ] + }, + { + "ct": 6500, + "table": + [ + 1.567, 1.565, 1.555, 1.541, 1.525, 1.518, 1.518, 1.518, 1.521, 1.527, 1.532, 1.541, 1.551, 1.559, 1.567, 1.569, + 1.565, 1.557, 1.542, 1.527, 1.519, 1.515, 1.511, 1.516, 1.519, 1.524, 1.528, 1.533, 1.542, 1.553, 1.559, 1.562, + 1.561, 1.546, 1.532, 1.521, 1.518, 1.515, 1.511, 1.516, 1.519, 1.524, 1.528, 1.529, 1.533, 1.542, 1.554, 1.559, + 1.561, 1.539, 1.526, 1.524, 1.521, 1.521, 1.522, 1.524, 1.525, 1.531, 1.529, 1.529, 1.531, 1.538, 1.549, 1.558, + 1.559, 1.538, 1.526, 1.525, 1.524, 1.528, 1.534, 1.536, 1.536, 1.536, 1.532, 1.529, 1.531, 1.537, 1.548, 1.556, + 1.561, 1.537, 1.525, 1.524, 1.526, 1.532, 1.537, 1.539, 1.538, 1.537, 1.532, 1.529, 1.529, 1.537, 1.546, 1.556, + 1.561, 1.536, 1.524, 1.522, 1.525, 1.532, 1.538, 1.538, 1.537, 1.533, 1.528, 1.526, 1.527, 1.536, 1.546, 1.555, + 1.561, 1.537, 1.522, 1.521, 1.524, 1.531, 1.536, 1.537, 1.534, 1.529, 1.526, 1.522, 1.523, 1.534, 1.547, 1.555, + 1.561, 1.538, 1.524, 1.522, 1.526, 1.531, 1.535, 1.535, 1.534, 1.527, 1.524, 1.522, 1.522, 1.535, 1.549, 1.556, + 1.558, 1.543, 1.532, 1.526, 1.526, 1.529, 1.534, 1.535, 1.533, 1.526, 1.523, 1.522, 1.524, 1.537, 1.552, 1.557, + 1.555, 1.546, 1.541, 1.528, 1.527, 1.528, 1.531, 1.533, 1.531, 1.527, 1.522, 1.522, 1.526, 1.536, 1.552, 1.561, + 1.555, 1.547, 1.542, 1.538, 1.526, 1.526, 1.529, 1.531, 1.529, 1.528, 1.519, 1.519, 1.527, 1.531, 1.543, 1.561 + ] + } + ], + "calibrations_Cb": [ + { + "ct": 3000, + "table": + [ + 1.684, 1.688, 1.691, 1.697, 1.709, 1.722, 1.735, 1.745, 1.747, 1.745, 1.731, 1.719, 1.709, 1.705, 1.699, 1.699, + 1.684, 1.689, 1.694, 1.708, 1.721, 1.735, 1.747, 1.762, 1.762, 1.758, 1.745, 1.727, 1.716, 1.707, 1.701, 1.699, + 1.684, 1.691, 1.704, 1.719, 1.734, 1.755, 1.772, 1.786, 1.789, 1.788, 1.762, 1.745, 1.724, 1.709, 1.702, 1.698, + 1.682, 1.694, 1.709, 1.729, 1.755, 1.773, 1.798, 1.815, 1.817, 1.808, 1.788, 1.762, 1.733, 1.714, 1.704, 1.699, + 1.682, 1.693, 1.713, 1.742, 1.772, 1.798, 1.815, 1.829, 1.831, 1.821, 1.807, 1.773, 1.742, 1.716, 1.703, 1.699, + 1.681, 1.693, 1.713, 1.742, 1.772, 1.799, 1.828, 1.839, 1.839, 1.828, 1.807, 1.774, 1.742, 1.715, 1.699, 1.695, + 1.679, 1.691, 1.712, 1.739, 1.771, 1.798, 1.825, 1.829, 1.831, 1.818, 1.801, 1.774, 1.738, 1.712, 1.695, 1.691, + 1.676, 1.685, 1.703, 1.727, 1.761, 1.784, 1.801, 1.817, 1.817, 1.801, 1.779, 1.761, 1.729, 1.706, 1.691, 1.684, + 1.669, 1.678, 1.692, 1.714, 1.741, 1.764, 1.784, 1.795, 1.795, 1.779, 1.761, 1.738, 1.713, 1.696, 1.683, 1.679, + 1.664, 1.671, 1.679, 1.693, 1.716, 1.741, 1.762, 1.769, 1.769, 1.753, 1.738, 1.713, 1.701, 1.687, 1.681, 1.676, + 1.661, 1.664, 1.671, 1.679, 1.693, 1.714, 1.732, 1.739, 1.739, 1.729, 1.708, 1.701, 1.685, 1.679, 1.676, 1.677, + 1.659, 1.661, 1.664, 1.671, 1.679, 1.693, 1.712, 1.714, 1.714, 1.708, 1.701, 1.687, 1.679, 1.672, 1.673, 1.677 + ] + }, + { + "ct": 5000, + "table": + [ + 1.177, 1.183, 1.187, 1.191, 1.197, 1.206, 1.213, 1.215, 1.215, 1.215, 1.211, 1.204, 1.196, 1.191, 1.183, 1.182, + 1.179, 1.185, 1.191, 1.196, 1.206, 1.217, 1.224, 1.229, 1.229, 1.226, 1.221, 1.212, 1.202, 1.195, 1.188, 1.182, + 1.183, 1.191, 1.196, 1.206, 1.217, 1.229, 1.239, 1.245, 1.245, 1.245, 1.233, 1.221, 1.212, 1.199, 1.193, 1.187, + 1.183, 1.192, 1.201, 1.212, 1.229, 1.241, 1.252, 1.259, 1.259, 1.257, 1.245, 1.233, 1.217, 1.201, 1.194, 1.192, + 1.183, 1.192, 1.202, 1.219, 1.238, 1.252, 1.261, 1.269, 1.268, 1.261, 1.257, 1.241, 1.223, 1.204, 1.194, 1.191, + 1.182, 1.192, 1.202, 1.219, 1.239, 1.255, 1.266, 1.271, 1.271, 1.265, 1.258, 1.242, 1.223, 1.205, 1.192, 1.191, + 1.181, 1.189, 1.199, 1.218, 1.239, 1.254, 1.262, 1.268, 1.268, 1.258, 1.253, 1.241, 1.221, 1.204, 1.191, 1.187, + 1.179, 1.184, 1.193, 1.211, 1.232, 1.243, 1.254, 1.257, 1.256, 1.253, 1.242, 1.232, 1.216, 1.199, 1.187, 1.183, + 1.174, 1.179, 1.187, 1.202, 1.218, 1.232, 1.243, 1.246, 1.246, 1.239, 1.232, 1.218, 1.207, 1.191, 1.183, 1.179, + 1.169, 1.175, 1.181, 1.189, 1.202, 1.218, 1.229, 1.232, 1.232, 1.224, 1.218, 1.207, 1.199, 1.185, 1.181, 1.174, + 1.164, 1.168, 1.175, 1.179, 1.189, 1.201, 1.209, 1.213, 1.213, 1.209, 1.201, 1.198, 1.186, 1.181, 1.174, 1.173, + 1.161, 1.166, 1.171, 1.175, 1.179, 1.189, 1.197, 1.198, 1.198, 1.197, 1.196, 1.186, 1.182, 1.175, 1.173, 1.173 + ] + }, + { + "ct": 6500, + "table": + [ + 1.166, 1.171, 1.173, 1.178, 1.187, 1.193, 1.201, 1.205, 1.205, 1.205, 1.199, 1.191, 1.184, 1.179, 1.174, 1.171, + 1.166, 1.172, 1.176, 1.184, 1.195, 1.202, 1.209, 1.216, 1.216, 1.213, 1.208, 1.201, 1.189, 1.182, 1.176, 1.171, + 1.166, 1.173, 1.183, 1.195, 1.202, 1.214, 1.221, 1.228, 1.229, 1.228, 1.221, 1.209, 1.201, 1.186, 1.179, 1.174, + 1.165, 1.174, 1.187, 1.201, 1.214, 1.223, 1.235, 1.241, 1.242, 1.241, 1.229, 1.221, 1.205, 1.188, 1.181, 1.177, + 1.165, 1.174, 1.189, 1.207, 1.223, 1.235, 1.242, 1.253, 1.252, 1.245, 1.241, 1.228, 1.211, 1.189, 1.181, 1.178, + 1.164, 1.173, 1.189, 1.207, 1.224, 1.238, 1.249, 1.255, 1.255, 1.249, 1.242, 1.228, 1.211, 1.191, 1.179, 1.176, + 1.163, 1.172, 1.187, 1.207, 1.223, 1.237, 1.245, 1.253, 1.252, 1.243, 1.237, 1.228, 1.207, 1.188, 1.176, 1.173, + 1.159, 1.167, 1.179, 1.199, 1.217, 1.227, 1.237, 1.241, 1.241, 1.237, 1.228, 1.217, 1.201, 1.184, 1.174, 1.169, + 1.156, 1.164, 1.172, 1.189, 1.205, 1.217, 1.226, 1.229, 1.229, 1.222, 1.217, 1.204, 1.192, 1.177, 1.171, 1.166, + 1.154, 1.159, 1.166, 1.177, 1.189, 1.205, 1.213, 1.216, 1.216, 1.209, 1.204, 1.192, 1.183, 1.172, 1.168, 1.162, + 1.152, 1.155, 1.161, 1.166, 1.177, 1.188, 1.195, 1.198, 1.199, 1.196, 1.187, 1.183, 1.173, 1.168, 1.163, 1.162, + 1.151, 1.154, 1.158, 1.162, 1.168, 1.177, 1.183, 1.184, 1.184, 1.184, 1.182, 1.172, 1.168, 1.165, 1.162, 1.161 + ] + } + ], + "luminance_lut": + [ + 2.236, 2.111, 1.912, 1.741, 1.579, 1.451, 1.379, 1.349, 1.349, 1.361, 1.411, 1.505, 1.644, 1.816, 2.034, 2.159, + 2.139, 1.994, 1.796, 1.625, 1.467, 1.361, 1.285, 1.248, 1.239, 1.265, 1.321, 1.408, 1.536, 1.703, 1.903, 2.087, + 2.047, 1.898, 1.694, 1.511, 1.373, 1.254, 1.186, 1.152, 1.142, 1.166, 1.226, 1.309, 1.441, 1.598, 1.799, 1.978, + 1.999, 1.824, 1.615, 1.429, 1.281, 1.179, 1.113, 1.077, 1.071, 1.096, 1.153, 1.239, 1.357, 1.525, 1.726, 1.915, + 1.976, 1.773, 1.563, 1.374, 1.222, 1.119, 1.064, 1.032, 1.031, 1.049, 1.099, 1.188, 1.309, 1.478, 1.681, 1.893, + 1.973, 1.756, 1.542, 1.351, 1.196, 1.088, 1.028, 1.011, 1.004, 1.029, 1.077, 1.169, 1.295, 1.459, 1.663, 1.891, + 1.973, 1.761, 1.541, 1.349, 1.193, 1.087, 1.031, 1.006, 1.006, 1.023, 1.075, 1.169, 1.298, 1.463, 1.667, 1.891, + 1.982, 1.789, 1.568, 1.373, 1.213, 1.111, 1.051, 1.029, 1.024, 1.053, 1.106, 1.199, 1.329, 1.495, 1.692, 1.903, + 2.015, 1.838, 1.621, 1.426, 1.268, 1.159, 1.101, 1.066, 1.068, 1.099, 1.166, 1.259, 1.387, 1.553, 1.751, 1.937, + 2.076, 1.911, 1.692, 1.507, 1.346, 1.236, 1.169, 1.136, 1.139, 1.174, 1.242, 1.349, 1.475, 1.641, 1.833, 2.004, + 2.193, 2.011, 1.798, 1.604, 1.444, 1.339, 1.265, 1.235, 1.237, 1.273, 1.351, 1.461, 1.598, 1.758, 1.956, 2.125, + 2.263, 2.154, 1.916, 1.711, 1.549, 1.432, 1.372, 1.356, 1.356, 1.383, 1.455, 1.578, 1.726, 1.914, 2.119, 2.211 + ], + "sigma": 0.006, + "sigma_Cb": 0.00208 + } + }, + { + "rpi.contrast": + { + "ce_enable": 1, + "gamma_curve": + [ + 0, 0, + 1024, 5040, + 2048, 9338, + 3072, 12356, + 4096, 15312, + 5120, 18051, + 6144, 20790, + 7168, 23193, + 8192, 25744, + 9216, 27942, + 10240, 30035, + 11264, 32005, + 12288, 33975, + 13312, 35815, + 14336, 37600, + 15360, 39168, + 16384, 40642, + 18432, 43379, + 20480, 45749, + 22528, 47753, + 24576, 49621, + 26624, 51253, + 28672, 52698, + 30720, 53796, + 32768, 54876, + 36864, 57012, + 40960, 58656, + 45056, 59954, + 49152, 61183, + 53248, 62355, + 57344, 63419, + 61440, 64476, + 65535, 65535 + ] + } + }, + { + "rpi.ccm": + { + "ccms": [ + { + "ct": 2500, + "ccm": + [ + 1.70741, -0.05307, -0.65433, + -0.62822, 1.68836, -0.06014, + -0.04452, -1.87628, 2.92079 + ] + }, + { + "ct": 2803, + "ccm": + [ + 1.74383, -0.18731, -0.55652, + -0.56491, 1.67772, -0.11281, + -0.01522, -1.60635, 2.62157 + ] + }, + { + "ct": 2912, + "ccm": + [ + 1.75215, -0.22221, -0.52995, + -0.54568, 1.63522, -0.08954, + 0.02633, -1.56997, 2.54364 + ] + }, + { + "ct": 2914, + "ccm": + [ + 1.72423, -0.28939, -0.43484, + -0.55188, 1.62925, -0.07737, + 0.01959, -1.28661, 2.26702 + ] + }, + { + "ct": 3605, + "ccm": + [ + 1.80381, -0.43646, -0.36735, + -0.46505, 1.56814, -0.10309, + 0.00929, -1.00424, 1.99495 + ] + }, + { + "ct": 4540, + "ccm": + [ + 1.85263, -0.46545, -0.38719, + -0.44136, 1.68443, -0.24307, + 0.04108, -0.85599, 1.81491 + ] + }, + { + "ct": 5699, + "ccm": + [ + 1.98595, -0.63542, -0.35054, + -0.34623, 1.54146, -0.19522, + 0.00411, -0.70936, 1.70525 + ] + }, + { + "ct": 8625, + "ccm": + [ + 2.21637, -0.56663, -0.64974, + -0.41133, 1.96625, -0.55492, + -0.02307, -0.83529, 1.85837 + ] + } + ] + } + }, + { + "rpi.sharpen": { } + } + ] +} \ No newline at end of file diff --git a/src/ipa/rpi/vc4/data/ov5647_noir.json b/src/ipa/rpi/vc4/data/ov5647_noir.json new file mode 100644 index 00000000..a6c6722f --- /dev/null +++ b/src/ipa/rpi/vc4/data/ov5647_noir.json @@ -0,0 +1,403 @@ +{ + "version": 2.0, + "target": "bcm2835", + "algorithms": [ + { + "rpi.black_level": + { + "black_level": 1024 + } + }, + { + "rpi.dpc": { } + }, + { + "rpi.lux": + { + "reference_shutter_speed": 21663, + "reference_gain": 1.0, + "reference_aperture": 1.0, + "reference_lux": 987, + "reference_Y": 8961 + } + }, + { + "rpi.noise": + { + "reference_constant": 0, + "reference_slope": 4.25 + } + }, + { + "rpi.geq": + { + "offset": 401, + "slope": 0.05619 + } + }, + { + "rpi.sdn": { } + }, + { + "rpi.awb": + { + "bayes": 0 + } + }, + { + "rpi.agc": + { + "metering_modes": + { + "centre-weighted": + { + "weights": [ 3, 3, 3, 2, 2, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0 ] + }, + "spot": + { + "weights": [ 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ] + }, + "matrix": + { + "weights": [ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ] + } + }, + "exposure_modes": + { + "normal": + { + "shutter": [ 100, 10000, 30000, 60000, 66666 ], + "gain": [ 1.0, 2.0, 4.0, 6.0, 8.0 ] + }, + "short": + { + "shutter": [ 100, 5000, 10000, 20000, 33333 ], + "gain": [ 1.0, 2.0, 4.0, 6.0, 8.0 ] + }, + "long": + { + "shutter": [ 100, 10000, 30000, 60000, 120000 ], + "gain": [ 1.0, 2.0, 4.0, 6.0, 12.0 ] + } + }, + "constraint_modes": + { + "normal": [ + { + "bound": "LOWER", + "q_lo": 0.98, + "q_hi": 1.0, + "y_target": + [ + 0, 0.5, + 1000, 0.5 + ] + } + ], + "highlight": [ + { + "bound": "LOWER", + "q_lo": 0.98, + "q_hi": 1.0, + "y_target": + [ + 0, 0.5, + 1000, 0.5 + ] + }, + { + "bound": "UPPER", + "q_lo": 0.98, + "q_hi": 1.0, + "y_target": + [ + 0, 0.8, + 1000, 0.8 + ] + } + ], + "shadows": [ + { + "bound": "LOWER", + "q_lo": 0.0, + "q_hi": 0.5, + "y_target": + [ + 0, 0.17, + 1000, 0.17 + ] + } + ] + }, + "y_target": + [ + 0, 0.16, + 1000, 0.165, + 10000, 0.17 + ], + "base_ev": 1.25 + } + }, + { + "rpi.alsc": + { + "omega": 1.3, + "n_iter": 100, + "luminance_strength": 0.5, + "calibrations_Cr": [ + { + "ct": 3000, + "table": + [ + 1.105, 1.103, 1.093, 1.083, 1.071, 1.065, 1.065, 1.065, 1.066, 1.069, 1.072, 1.077, 1.084, 1.089, 1.093, 1.093, + 1.103, 1.096, 1.084, 1.072, 1.059, 1.051, 1.047, 1.047, 1.051, 1.053, 1.059, 1.067, 1.075, 1.082, 1.085, 1.086, + 1.096, 1.084, 1.072, 1.059, 1.051, 1.045, 1.039, 1.038, 1.039, 1.045, 1.049, 1.057, 1.063, 1.072, 1.081, 1.082, + 1.092, 1.075, 1.061, 1.052, 1.045, 1.039, 1.036, 1.035, 1.035, 1.039, 1.044, 1.049, 1.056, 1.063, 1.072, 1.081, + 1.092, 1.073, 1.058, 1.048, 1.043, 1.038, 1.035, 1.033, 1.033, 1.035, 1.039, 1.044, 1.051, 1.057, 1.069, 1.078, + 1.091, 1.068, 1.054, 1.045, 1.041, 1.038, 1.035, 1.032, 1.032, 1.032, 1.036, 1.041, 1.045, 1.055, 1.069, 1.078, + 1.091, 1.068, 1.052, 1.043, 1.041, 1.038, 1.035, 1.032, 1.031, 1.032, 1.034, 1.036, 1.043, 1.055, 1.069, 1.078, + 1.092, 1.068, 1.052, 1.047, 1.042, 1.041, 1.038, 1.035, 1.032, 1.032, 1.035, 1.039, 1.043, 1.055, 1.071, 1.079, + 1.092, 1.073, 1.057, 1.051, 1.047, 1.047, 1.044, 1.041, 1.038, 1.038, 1.039, 1.043, 1.051, 1.059, 1.076, 1.083, + 1.092, 1.081, 1.068, 1.058, 1.056, 1.056, 1.053, 1.052, 1.049, 1.048, 1.048, 1.051, 1.059, 1.066, 1.083, 1.085, + 1.091, 1.087, 1.081, 1.068, 1.065, 1.064, 1.062, 1.062, 1.061, 1.056, 1.056, 1.056, 1.064, 1.069, 1.084, 1.089, + 1.091, 1.089, 1.085, 1.079, 1.069, 1.068, 1.067, 1.067, 1.067, 1.063, 1.061, 1.063, 1.068, 1.069, 1.081, 1.092 + ] + }, + { + "ct": 5000, + "table": + [ + 1.486, 1.484, 1.468, 1.449, 1.427, 1.403, 1.399, 1.399, 1.399, 1.404, 1.413, 1.433, 1.454, 1.473, 1.482, 1.488, + 1.484, 1.472, 1.454, 1.431, 1.405, 1.381, 1.365, 1.365, 1.367, 1.373, 1.392, 1.411, 1.438, 1.458, 1.476, 1.481, + 1.476, 1.458, 1.433, 1.405, 1.381, 1.361, 1.339, 1.334, 1.334, 1.346, 1.362, 1.391, 1.411, 1.438, 1.462, 1.474, + 1.471, 1.443, 1.417, 1.388, 1.361, 1.339, 1.321, 1.313, 1.313, 1.327, 1.346, 1.362, 1.391, 1.422, 1.453, 1.473, + 1.469, 1.439, 1.408, 1.377, 1.349, 1.321, 1.312, 1.299, 1.299, 1.311, 1.327, 1.348, 1.378, 1.415, 1.446, 1.468, + 1.468, 1.434, 1.402, 1.371, 1.341, 1.316, 1.299, 1.296, 1.295, 1.299, 1.314, 1.338, 1.371, 1.408, 1.441, 1.466, + 1.468, 1.434, 1.401, 1.371, 1.341, 1.316, 1.301, 1.296, 1.295, 1.297, 1.314, 1.338, 1.369, 1.408, 1.441, 1.465, + 1.469, 1.436, 1.401, 1.374, 1.348, 1.332, 1.315, 1.301, 1.301, 1.313, 1.324, 1.342, 1.372, 1.409, 1.442, 1.465, + 1.471, 1.444, 1.413, 1.388, 1.371, 1.348, 1.332, 1.323, 1.323, 1.324, 1.342, 1.362, 1.386, 1.418, 1.449, 1.467, + 1.473, 1.454, 1.431, 1.407, 1.388, 1.371, 1.359, 1.352, 1.351, 1.351, 1.362, 1.383, 1.404, 1.433, 1.462, 1.472, + 1.474, 1.461, 1.447, 1.424, 1.407, 1.394, 1.385, 1.381, 1.379, 1.381, 1.383, 1.401, 1.419, 1.444, 1.466, 1.481, + 1.474, 1.464, 1.455, 1.442, 1.421, 1.408, 1.403, 1.403, 1.403, 1.399, 1.402, 1.415, 1.432, 1.446, 1.467, 1.483 + ] + }, + { + "ct": 6500, + "table": + [ + 1.567, 1.565, 1.555, 1.541, 1.525, 1.518, 1.518, 1.518, 1.521, 1.527, 1.532, 1.541, 1.551, 1.559, 1.567, 1.569, + 1.565, 1.557, 1.542, 1.527, 1.519, 1.515, 1.511, 1.516, 1.519, 1.524, 1.528, 1.533, 1.542, 1.553, 1.559, 1.562, + 1.561, 1.546, 1.532, 1.521, 1.518, 1.515, 1.511, 1.516, 1.519, 1.524, 1.528, 1.529, 1.533, 1.542, 1.554, 1.559, + 1.561, 1.539, 1.526, 1.524, 1.521, 1.521, 1.522, 1.524, 1.525, 1.531, 1.529, 1.529, 1.531, 1.538, 1.549, 1.558, + 1.559, 1.538, 1.526, 1.525, 1.524, 1.528, 1.534, 1.536, 1.536, 1.536, 1.532, 1.529, 1.531, 1.537, 1.548, 1.556, + 1.561, 1.537, 1.525, 1.524, 1.526, 1.532, 1.537, 1.539, 1.538, 1.537, 1.532, 1.529, 1.529, 1.537, 1.546, 1.556, + 1.561, 1.536, 1.524, 1.522, 1.525, 1.532, 1.538, 1.538, 1.537, 1.533, 1.528, 1.526, 1.527, 1.536, 1.546, 1.555, + 1.561, 1.537, 1.522, 1.521, 1.524, 1.531, 1.536, 1.537, 1.534, 1.529, 1.526, 1.522, 1.523, 1.534, 1.547, 1.555, + 1.561, 1.538, 1.524, 1.522, 1.526, 1.531, 1.535, 1.535, 1.534, 1.527, 1.524, 1.522, 1.522, 1.535, 1.549, 1.556, + 1.558, 1.543, 1.532, 1.526, 1.526, 1.529, 1.534, 1.535, 1.533, 1.526, 1.523, 1.522, 1.524, 1.537, 1.552, 1.557, + 1.555, 1.546, 1.541, 1.528, 1.527, 1.528, 1.531, 1.533, 1.531, 1.527, 1.522, 1.522, 1.526, 1.536, 1.552, 1.561, + 1.555, 1.547, 1.542, 1.538, 1.526, 1.526, 1.529, 1.531, 1.529, 1.528, 1.519, 1.519, 1.527, 1.531, 1.543, 1.561 + ] + } + ], + "calibrations_Cb": [ + { + "ct": 3000, + "table": + [ + 1.684, 1.688, 1.691, 1.697, 1.709, 1.722, 1.735, 1.745, 1.747, 1.745, 1.731, 1.719, 1.709, 1.705, 1.699, 1.699, + 1.684, 1.689, 1.694, 1.708, 1.721, 1.735, 1.747, 1.762, 1.762, 1.758, 1.745, 1.727, 1.716, 1.707, 1.701, 1.699, + 1.684, 1.691, 1.704, 1.719, 1.734, 1.755, 1.772, 1.786, 1.789, 1.788, 1.762, 1.745, 1.724, 1.709, 1.702, 1.698, + 1.682, 1.694, 1.709, 1.729, 1.755, 1.773, 1.798, 1.815, 1.817, 1.808, 1.788, 1.762, 1.733, 1.714, 1.704, 1.699, + 1.682, 1.693, 1.713, 1.742, 1.772, 1.798, 1.815, 1.829, 1.831, 1.821, 1.807, 1.773, 1.742, 1.716, 1.703, 1.699, + 1.681, 1.693, 1.713, 1.742, 1.772, 1.799, 1.828, 1.839, 1.839, 1.828, 1.807, 1.774, 1.742, 1.715, 1.699, 1.695, + 1.679, 1.691, 1.712, 1.739, 1.771, 1.798, 1.825, 1.829, 1.831, 1.818, 1.801, 1.774, 1.738, 1.712, 1.695, 1.691, + 1.676, 1.685, 1.703, 1.727, 1.761, 1.784, 1.801, 1.817, 1.817, 1.801, 1.779, 1.761, 1.729, 1.706, 1.691, 1.684, + 1.669, 1.678, 1.692, 1.714, 1.741, 1.764, 1.784, 1.795, 1.795, 1.779, 1.761, 1.738, 1.713, 1.696, 1.683, 1.679, + 1.664, 1.671, 1.679, 1.693, 1.716, 1.741, 1.762, 1.769, 1.769, 1.753, 1.738, 1.713, 1.701, 1.687, 1.681, 1.676, + 1.661, 1.664, 1.671, 1.679, 1.693, 1.714, 1.732, 1.739, 1.739, 1.729, 1.708, 1.701, 1.685, 1.679, 1.676, 1.677, + 1.659, 1.661, 1.664, 1.671, 1.679, 1.693, 1.712, 1.714, 1.714, 1.708, 1.701, 1.687, 1.679, 1.672, 1.673, 1.677 + ] + }, + { + "ct": 5000, + "table": + [ + 1.177, 1.183, 1.187, 1.191, 1.197, 1.206, 1.213, 1.215, 1.215, 1.215, 1.211, 1.204, 1.196, 1.191, 1.183, 1.182, + 1.179, 1.185, 1.191, 1.196, 1.206, 1.217, 1.224, 1.229, 1.229, 1.226, 1.221, 1.212, 1.202, 1.195, 1.188, 1.182, + 1.183, 1.191, 1.196, 1.206, 1.217, 1.229, 1.239, 1.245, 1.245, 1.245, 1.233, 1.221, 1.212, 1.199, 1.193, 1.187, + 1.183, 1.192, 1.201, 1.212, 1.229, 1.241, 1.252, 1.259, 1.259, 1.257, 1.245, 1.233, 1.217, 1.201, 1.194, 1.192, + 1.183, 1.192, 1.202, 1.219, 1.238, 1.252, 1.261, 1.269, 1.268, 1.261, 1.257, 1.241, 1.223, 1.204, 1.194, 1.191, + 1.182, 1.192, 1.202, 1.219, 1.239, 1.255, 1.266, 1.271, 1.271, 1.265, 1.258, 1.242, 1.223, 1.205, 1.192, 1.191, + 1.181, 1.189, 1.199, 1.218, 1.239, 1.254, 1.262, 1.268, 1.268, 1.258, 1.253, 1.241, 1.221, 1.204, 1.191, 1.187, + 1.179, 1.184, 1.193, 1.211, 1.232, 1.243, 1.254, 1.257, 1.256, 1.253, 1.242, 1.232, 1.216, 1.199, 1.187, 1.183, + 1.174, 1.179, 1.187, 1.202, 1.218, 1.232, 1.243, 1.246, 1.246, 1.239, 1.232, 1.218, 1.207, 1.191, 1.183, 1.179, + 1.169, 1.175, 1.181, 1.189, 1.202, 1.218, 1.229, 1.232, 1.232, 1.224, 1.218, 1.207, 1.199, 1.185, 1.181, 1.174, + 1.164, 1.168, 1.175, 1.179, 1.189, 1.201, 1.209, 1.213, 1.213, 1.209, 1.201, 1.198, 1.186, 1.181, 1.174, 1.173, + 1.161, 1.166, 1.171, 1.175, 1.179, 1.189, 1.197, 1.198, 1.198, 1.197, 1.196, 1.186, 1.182, 1.175, 1.173, 1.173 + ] + }, + { + "ct": 6500, + "table": + [ + 1.166, 1.171, 1.173, 1.178, 1.187, 1.193, 1.201, 1.205, 1.205, 1.205, 1.199, 1.191, 1.184, 1.179, 1.174, 1.171, + 1.166, 1.172, 1.176, 1.184, 1.195, 1.202, 1.209, 1.216, 1.216, 1.213, 1.208, 1.201, 1.189, 1.182, 1.176, 1.171, + 1.166, 1.173, 1.183, 1.195, 1.202, 1.214, 1.221, 1.228, 1.229, 1.228, 1.221, 1.209, 1.201, 1.186, 1.179, 1.174, + 1.165, 1.174, 1.187, 1.201, 1.214, 1.223, 1.235, 1.241, 1.242, 1.241, 1.229, 1.221, 1.205, 1.188, 1.181, 1.177, + 1.165, 1.174, 1.189, 1.207, 1.223, 1.235, 1.242, 1.253, 1.252, 1.245, 1.241, 1.228, 1.211, 1.189, 1.181, 1.178, + 1.164, 1.173, 1.189, 1.207, 1.224, 1.238, 1.249, 1.255, 1.255, 1.249, 1.242, 1.228, 1.211, 1.191, 1.179, 1.176, + 1.163, 1.172, 1.187, 1.207, 1.223, 1.237, 1.245, 1.253, 1.252, 1.243, 1.237, 1.228, 1.207, 1.188, 1.176, 1.173, + 1.159, 1.167, 1.179, 1.199, 1.217, 1.227, 1.237, 1.241, 1.241, 1.237, 1.228, 1.217, 1.201, 1.184, 1.174, 1.169, + 1.156, 1.164, 1.172, 1.189, 1.205, 1.217, 1.226, 1.229, 1.229, 1.222, 1.217, 1.204, 1.192, 1.177, 1.171, 1.166, + 1.154, 1.159, 1.166, 1.177, 1.189, 1.205, 1.213, 1.216, 1.216, 1.209, 1.204, 1.192, 1.183, 1.172, 1.168, 1.162, + 1.152, 1.155, 1.161, 1.166, 1.177, 1.188, 1.195, 1.198, 1.199, 1.196, 1.187, 1.183, 1.173, 1.168, 1.163, 1.162, + 1.151, 1.154, 1.158, 1.162, 1.168, 1.177, 1.183, 1.184, 1.184, 1.184, 1.182, 1.172, 1.168, 1.165, 1.162, 1.161 + ] + } + ], + "luminance_lut": + [ + 2.236, 2.111, 1.912, 1.741, 1.579, 1.451, 1.379, 1.349, 1.349, 1.361, 1.411, 1.505, 1.644, 1.816, 2.034, 2.159, + 2.139, 1.994, 1.796, 1.625, 1.467, 1.361, 1.285, 1.248, 1.239, 1.265, 1.321, 1.408, 1.536, 1.703, 1.903, 2.087, + 2.047, 1.898, 1.694, 1.511, 1.373, 1.254, 1.186, 1.152, 1.142, 1.166, 1.226, 1.309, 1.441, 1.598, 1.799, 1.978, + 1.999, 1.824, 1.615, 1.429, 1.281, 1.179, 1.113, 1.077, 1.071, 1.096, 1.153, 1.239, 1.357, 1.525, 1.726, 1.915, + 1.976, 1.773, 1.563, 1.374, 1.222, 1.119, 1.064, 1.032, 1.031, 1.049, 1.099, 1.188, 1.309, 1.478, 1.681, 1.893, + 1.973, 1.756, 1.542, 1.351, 1.196, 1.088, 1.028, 1.011, 1.004, 1.029, 1.077, 1.169, 1.295, 1.459, 1.663, 1.891, + 1.973, 1.761, 1.541, 1.349, 1.193, 1.087, 1.031, 1.006, 1.006, 1.023, 1.075, 1.169, 1.298, 1.463, 1.667, 1.891, + 1.982, 1.789, 1.568, 1.373, 1.213, 1.111, 1.051, 1.029, 1.024, 1.053, 1.106, 1.199, 1.329, 1.495, 1.692, 1.903, + 2.015, 1.838, 1.621, 1.426, 1.268, 1.159, 1.101, 1.066, 1.068, 1.099, 1.166, 1.259, 1.387, 1.553, 1.751, 1.937, + 2.076, 1.911, 1.692, 1.507, 1.346, 1.236, 1.169, 1.136, 1.139, 1.174, 1.242, 1.349, 1.475, 1.641, 1.833, 2.004, + 2.193, 2.011, 1.798, 1.604, 1.444, 1.339, 1.265, 1.235, 1.237, 1.273, 1.351, 1.461, 1.598, 1.758, 1.956, 2.125, + 2.263, 2.154, 1.916, 1.711, 1.549, 1.432, 1.372, 1.356, 1.356, 1.383, 1.455, 1.578, 1.726, 1.914, 2.119, 2.211 + ], + "sigma": 0.006, + "sigma_Cb": 0.00208 + } + }, + { + "rpi.contrast": + { + "ce_enable": 1, + "gamma_curve": + [ + 0, 0, + 1024, 5040, + 2048, 9338, + 3072, 12356, + 4096, 15312, + 5120, 18051, + 6144, 20790, + 7168, 23193, + 8192, 25744, + 9216, 27942, + 10240, 30035, + 11264, 32005, + 12288, 33975, + 13312, 35815, + 14336, 37600, + 15360, 39168, + 16384, 40642, + 18432, 43379, + 20480, 45749, + 22528, 47753, + 24576, 49621, + 26624, 51253, + 28672, 52698, + 30720, 53796, + 32768, 54876, + 36864, 57012, + 40960, 58656, + 45056, 59954, + 49152, 61183, + 53248, 62355, + 57344, 63419, + 61440, 64476, + 65535, 65535 + ] + } + }, + { + "rpi.ccm": + { + "ccms": [ + { + "ct": 2500, + "ccm": + [ + 1.70741, -0.05307, -0.65433, + -0.62822, 1.68836, -0.06014, + -0.04452, -1.87628, 2.92079 + ] + }, + { + "ct": 2803, + "ccm": + [ + 1.74383, -0.18731, -0.55652, + -0.56491, 1.67772, -0.11281, + -0.01522, -1.60635, 2.62157 + ] + }, + { + "ct": 2912, + "ccm": + [ + 1.75215, -0.22221, -0.52995, + -0.54568, 1.63522, -0.08954, + 0.02633, -1.56997, 2.54364 + ] + }, + { + "ct": 2914, + "ccm": + [ + 1.72423, -0.28939, -0.43484, + -0.55188, 1.62925, -0.07737, + 0.01959, -1.28661, 2.26702 + ] + }, + { + "ct": 3605, + "ccm": + [ + 1.80381, -0.43646, -0.36735, + -0.46505, 1.56814, -0.10309, + 0.00929, -1.00424, 1.99495 + ] + }, + { + "ct": 4540, + "ccm": + [ + 1.85263, -0.46545, -0.38719, + -0.44136, 1.68443, -0.24307, + 0.04108, -0.85599, 1.81491 + ] + }, + { + "ct": 5699, + "ccm": + [ + 1.98595, -0.63542, -0.35054, + -0.34623, 1.54146, -0.19522, + 0.00411, -0.70936, 1.70525 + ] + }, + { + "ct": 8625, + "ccm": + [ + 2.21637, -0.56663, -0.64974, + -0.41133, 1.96625, -0.55492, + -0.02307, -0.83529, 1.85837 + ] + } + ] + } + }, + { + "rpi.sharpen": { } + } + ] +} \ No newline at end of file diff --git a/src/ipa/rpi/vc4/data/ov9281_mono.json b/src/ipa/rpi/vc4/data/ov9281_mono.json new file mode 100644 index 00000000..37944c63 --- /dev/null +++ b/src/ipa/rpi/vc4/data/ov9281_mono.json @@ -0,0 +1,123 @@ +{ + "version": 2.0, + "target": "bcm2835", + "algorithms": [ + { + "rpi.black_level": + { + "black_level": 4096 + } + }, + { + "rpi.lux": + { + "reference_shutter_speed": 2000, + "reference_gain": 1.0, + "reference_aperture": 1.0, + "reference_lux": 800, + "reference_Y": 20000 + } + }, + { + "rpi.noise": + { + "reference_constant": 0, + "reference_slope": 2.5 + } + }, + { + "rpi.sdn": { } + }, + { + "rpi.agc": + { + "metering_modes": + { + "centre-weighted": + { + "weights": [ 4, 4, 4, 2, 2, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0 ] + } + }, + "exposure_modes": + { + "normal": + { + "shutter": [ 100, 15000, 30000, 60000, 120000 ], + "gain": [ 1.0, 2.0, 3.0, 4.0, 8.0 ] + } + }, + "constraint_modes": + { + "normal": [ + { + "bound": "LOWER", + "q_lo": 0.98, + "q_hi": 1.0, + "y_target": + [ + 0, 0.4, + 1000, 0.4 + ] + } + ] + }, + "y_target": + [ + 0, 0.16, + 1000, 0.165, + 10000, 0.17 + ] + } + }, + { + "rpi.alsc": + { + "n_iter": 0, + "luminance_strength": 1.0, + "corner_strength": 1.5 + } + }, + { + "rpi.contrast": + { + "ce_enable": 0, + "gamma_curve": + [ + 0, 0, + 1024, 5040, + 2048, 9338, + 3072, 12356, + 4096, 15312, + 5120, 18051, + 6144, 20790, + 7168, 23193, + 8192, 25744, + 9216, 27942, + 10240, 30035, + 11264, 32005, + 12288, 33975, + 13312, 35815, + 14336, 37600, + 15360, 39168, + 16384, 40642, + 18432, 43379, + 20480, 45749, + 22528, 47753, + 24576, 49621, + 26624, 51253, + 28672, 52698, + 30720, 53796, + 32768, 54876, + 36864, 57012, + 40960, 58656, + 45056, 59954, + 49152, 61183, + 53248, 62355, + 57344, 63419, + 61440, 64476, + 65535, 65535 + ] + } + } + ] +} \ No newline at end of file diff --git a/src/ipa/rpi/vc4/data/se327m12.json b/src/ipa/rpi/vc4/data/se327m12.json new file mode 100644 index 00000000..ee69caea --- /dev/null +++ b/src/ipa/rpi/vc4/data/se327m12.json @@ -0,0 +1,418 @@ +{ + "version": 2.0, + "target": "bcm2835", + "algorithms": [ + { + "rpi.black_level": + { + "black_level": 3840 + } + }, + { + "rpi.dpc": { } + }, + { + "rpi.lux": + { + "reference_shutter_speed": 6873, + "reference_gain": 1.0, + "reference_aperture": 1.0, + "reference_lux": 800, + "reference_Y": 12293 + } + }, + { + "rpi.noise": + { + "reference_constant": 0, + "reference_slope": 1.986 + } + }, + { + "rpi.geq": + { + "offset": 207, + "slope": 0.00539 + } + }, + { + "rpi.sdn": { } + }, + { + "rpi.awb": + { + "priors": [ + { + "lux": 0, + "prior": + [ + 2000, 1.0, + 3000, 0.0, + 13000, 0.0 + ] + }, + { + "lux": 800, + "prior": + [ + 2000, 0.0, + 6000, 2.0, + 13000, 2.0 + ] + }, + { + "lux": 1500, + "prior": + [ + 2000, 0.0, + 4000, 1.0, + 6000, 6.0, + 6500, 7.0, + 7000, 1.0, + 13000, 1.0 + ] + } + ], + "modes": + { + "auto": + { + "lo": 2500, + "hi": 8000 + }, + "incandescent": + { + "lo": 2500, + "hi": 3000 + }, + "tungsten": + { + "lo": 3000, + "hi": 3500 + }, + "fluorescent": + { + "lo": 4000, + "hi": 4700 + }, + "indoor": + { + "lo": 3000, + "hi": 5000 + }, + "daylight": + { + "lo": 5500, + "hi": 6500 + }, + "cloudy": + { + "lo": 7000, + "hi": 8600 + } + }, + "bayes": 1, + "ct_curve": + [ + 2900.0, 0.9217, 0.3657, + 3600.0, 0.7876, 0.4651, + 4600.0, 0.6807, 0.5684, + 5800.0, 0.5937, 0.6724, + 8100.0, 0.5447, 0.7403 + ], + "sensitivity_r": 1.0, + "sensitivity_b": 1.0, + "transverse_pos": 0.0162, + "transverse_neg": 0.0204 + } + }, + { + "rpi.agc": + { + "metering_modes": + { + "centre-weighted": + { + "weights": [ 3, 3, 3, 2, 2, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0 ] + }, + "spot": + { + "weights": [ 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ] + }, + "matrix": + { + "weights": [ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ] + } + }, + "exposure_modes": + { + "normal": + { + "shutter": [ 100, 10000, 30000, 60000, 120000 ], + "gain": [ 1.0, 2.0, 4.0, 6.0, 8.0 ] + }, + "short": + { + "shutter": [ 100, 5000, 10000, 20000, 120000 ], + "gain": [ 1.0, 2.0, 4.0, 6.0, 8.0 ] + } + }, + "constraint_modes": + { + "normal": [ + { + "bound": "LOWER", + "q_lo": 0.98, + "q_hi": 1.0, + "y_target": + [ + 0, 0.5, + 1000, 0.5 + ] + } + ], + "highlight": [ + { + "bound": "LOWER", + "q_lo": 0.98, + "q_hi": 1.0, + "y_target": + [ + 0, 0.5, + 1000, 0.5 + ] + }, + { + "bound": "UPPER", + "q_lo": 0.98, + "q_hi": 1.0, + "y_target": + [ + 0, 0.8, + 1000, 0.8 + ] + } + ] + }, + "y_target": + [ + 0, 0.16, + 1000, 0.165, + 10000, 0.17 + ] + } + }, + { + "rpi.alsc": + { + "omega": 1.3, + "n_iter": 100, + "luminance_strength": 0.5, + "calibrations_Cr": [ + { + "ct": 4000, + "table": + [ + 1.481, 1.471, 1.449, 1.429, 1.416, 1.404, 1.394, 1.389, 1.389, 1.389, 1.392, 1.397, 1.404, 1.416, 1.429, 1.437, + 1.472, 1.456, 1.436, 1.418, 1.405, 1.394, 1.389, 1.384, 1.382, 1.382, 1.386, 1.388, 1.398, 1.407, 1.422, 1.429, + 1.465, 1.443, 1.426, 1.411, 1.397, 1.389, 1.383, 1.377, 1.377, 1.377, 1.379, 1.384, 1.388, 1.398, 1.411, 1.422, + 1.462, 1.441, 1.423, 1.409, 1.395, 1.385, 1.379, 1.376, 1.374, 1.374, 1.375, 1.379, 1.384, 1.394, 1.407, 1.418, + 1.461, 1.439, 1.421, 1.407, 1.394, 1.385, 1.381, 1.376, 1.373, 1.373, 1.373, 1.376, 1.381, 1.389, 1.403, 1.415, + 1.461, 1.439, 1.419, 1.404, 1.392, 1.384, 1.379, 1.376, 1.373, 1.372, 1.374, 1.375, 1.379, 1.389, 1.401, 1.413, + 1.461, 1.438, 1.419, 1.402, 1.389, 1.383, 1.377, 1.375, 1.373, 1.372, 1.372, 1.375, 1.381, 1.388, 1.401, 1.414, + 1.462, 1.438, 1.419, 1.403, 1.391, 1.381, 1.377, 1.374, 1.373, 1.373, 1.374, 1.376, 1.381, 1.389, 1.401, 1.414, + 1.462, 1.441, 1.423, 1.405, 1.392, 1.383, 1.377, 1.374, 1.373, 1.372, 1.373, 1.376, 1.382, 1.391, 1.402, 1.414, + 1.465, 1.444, 1.424, 1.407, 1.393, 1.382, 1.378, 1.373, 1.369, 1.369, 1.372, 1.375, 1.381, 1.389, 1.402, 1.417, + 1.469, 1.449, 1.427, 1.413, 1.396, 1.384, 1.381, 1.375, 1.371, 1.371, 1.373, 1.377, 1.385, 1.393, 1.407, 1.422, + 1.474, 1.456, 1.436, 1.419, 1.407, 1.391, 1.383, 1.379, 1.377, 1.377, 1.378, 1.381, 1.391, 1.404, 1.422, 1.426 + ] + }, + { + "ct": 5000, + "table": + [ + 1.742, 1.721, 1.689, 1.661, 1.639, 1.623, 1.613, 1.609, 1.607, 1.606, 1.609, 1.617, 1.626, 1.641, 1.665, 1.681, + 1.728, 1.703, 1.672, 1.645, 1.631, 1.614, 1.602, 1.599, 1.596, 1.597, 1.601, 1.608, 1.618, 1.631, 1.653, 1.671, + 1.713, 1.691, 1.658, 1.635, 1.618, 1.606, 1.595, 1.591, 1.588, 1.588, 1.591, 1.601, 1.608, 1.624, 1.641, 1.658, + 1.707, 1.681, 1.651, 1.627, 1.613, 1.599, 1.591, 1.585, 1.583, 1.584, 1.587, 1.591, 1.601, 1.615, 1.633, 1.655, + 1.699, 1.672, 1.644, 1.622, 1.606, 1.593, 1.586, 1.581, 1.579, 1.581, 1.583, 1.587, 1.597, 1.611, 1.631, 1.652, + 1.697, 1.665, 1.637, 1.617, 1.601, 1.589, 1.584, 1.579, 1.577, 1.578, 1.581, 1.585, 1.597, 1.607, 1.627, 1.652, + 1.697, 1.662, 1.634, 1.613, 1.599, 1.591, 1.583, 1.578, 1.576, 1.576, 1.579, 1.586, 1.597, 1.607, 1.628, 1.653, + 1.697, 1.662, 1.633, 1.613, 1.598, 1.589, 1.582, 1.578, 1.576, 1.577, 1.582, 1.589, 1.598, 1.611, 1.635, 1.655, + 1.701, 1.666, 1.636, 1.616, 1.602, 1.589, 1.583, 1.578, 1.577, 1.581, 1.583, 1.591, 1.601, 1.617, 1.639, 1.659, + 1.708, 1.671, 1.641, 1.618, 1.603, 1.591, 1.584, 1.581, 1.578, 1.581, 1.585, 1.594, 1.604, 1.622, 1.646, 1.666, + 1.714, 1.681, 1.648, 1.622, 1.608, 1.599, 1.591, 1.584, 1.583, 1.584, 1.589, 1.599, 1.614, 1.629, 1.653, 1.673, + 1.719, 1.691, 1.659, 1.631, 1.618, 1.606, 1.596, 1.591, 1.591, 1.593, 1.599, 1.608, 1.623, 1.642, 1.665, 1.681 + ] + } + ], + "calibrations_Cb": [ + { + "ct": 4000, + "table": + [ + 2.253, 2.267, 2.289, 2.317, 2.342, 2.359, 2.373, 2.381, 2.381, 2.378, 2.368, 2.361, 2.344, 2.337, 2.314, 2.301, + 2.262, 2.284, 2.314, 2.335, 2.352, 2.371, 2.383, 2.391, 2.393, 2.391, 2.381, 2.368, 2.361, 2.342, 2.322, 2.308, + 2.277, 2.303, 2.321, 2.346, 2.364, 2.381, 2.391, 2.395, 2.397, 2.397, 2.395, 2.381, 2.367, 2.354, 2.332, 2.321, + 2.277, 2.304, 2.327, 2.349, 2.369, 2.388, 2.393, 2.396, 2.396, 2.398, 2.396, 2.391, 2.376, 2.359, 2.339, 2.328, + 2.279, 2.311, 2.327, 2.354, 2.377, 2.389, 2.393, 2.397, 2.397, 2.398, 2.395, 2.393, 2.382, 2.363, 2.344, 2.332, + 2.282, 2.311, 2.329, 2.354, 2.377, 2.386, 2.396, 2.396, 2.395, 2.396, 2.397, 2.394, 2.383, 2.367, 2.346, 2.333, + 2.283, 2.314, 2.333, 2.353, 2.375, 2.389, 2.394, 2.395, 2.395, 2.395, 2.396, 2.394, 2.386, 2.368, 2.354, 2.336, + 2.287, 2.309, 2.331, 2.352, 2.373, 2.386, 2.394, 2.395, 2.395, 2.396, 2.396, 2.394, 2.384, 2.371, 2.354, 2.339, + 2.289, 2.307, 2.326, 2.347, 2.369, 2.385, 2.392, 2.397, 2.398, 2.398, 2.397, 2.392, 2.383, 2.367, 2.352, 2.337, + 2.286, 2.303, 2.322, 2.342, 2.361, 2.379, 2.389, 2.394, 2.397, 2.398, 2.396, 2.389, 2.381, 2.366, 2.346, 2.332, + 2.284, 2.291, 2.312, 2.329, 2.351, 2.372, 2.381, 2.389, 2.393, 2.394, 2.389, 2.385, 2.374, 2.362, 2.338, 2.325, + 2.283, 2.288, 2.305, 2.319, 2.339, 2.365, 2.374, 2.381, 2.384, 2.386, 2.385, 2.379, 2.368, 2.342, 2.325, 2.318 + ] + }, + { + "ct": 5000, + "table": + [ + 1.897, 1.919, 1.941, 1.969, 1.989, 2.003, 2.014, 2.019, 2.019, 2.017, 2.014, 2.008, 1.999, 1.988, 1.968, 1.944, + 1.914, 1.932, 1.957, 1.982, 1.998, 2.014, 2.023, 2.029, 2.031, 2.029, 2.022, 2.014, 2.006, 1.995, 1.976, 1.955, + 1.925, 1.951, 1.974, 1.996, 2.013, 2.027, 2.035, 2.039, 2.039, 2.038, 2.035, 2.026, 2.015, 2.002, 1.984, 1.963, + 1.932, 1.958, 1.986, 2.007, 2.024, 2.034, 2.041, 2.041, 2.045, 2.045, 2.042, 2.033, 2.023, 2.009, 1.995, 1.971, + 1.942, 1.964, 1.994, 2.012, 2.029, 2.038, 2.043, 2.046, 2.047, 2.046, 2.045, 2.039, 2.029, 2.014, 1.997, 1.977, + 1.946, 1.974, 1.999, 2.015, 2.031, 2.041, 2.046, 2.047, 2.048, 2.047, 2.044, 2.041, 2.031, 2.019, 1.999, 1.978, + 1.948, 1.975, 2.002, 2.018, 2.031, 2.041, 2.046, 2.047, 2.048, 2.048, 2.045, 2.041, 2.029, 2.019, 1.998, 1.978, + 1.948, 1.973, 2.002, 2.018, 2.029, 2.042, 2.045, 2.048, 2.048, 2.048, 2.044, 2.037, 2.027, 2.014, 1.993, 1.978, + 1.945, 1.969, 1.998, 2.015, 2.028, 2.037, 2.045, 2.046, 2.047, 2.044, 2.039, 2.033, 2.022, 2.008, 1.989, 1.971, + 1.939, 1.964, 1.991, 2.011, 2.024, 2.032, 2.036, 2.042, 2.042, 2.039, 2.035, 2.024, 2.012, 1.998, 1.977, 1.964, + 1.932, 1.953, 1.981, 2.006, 2.016, 2.024, 2.028, 2.031, 2.034, 2.031, 2.024, 2.015, 2.005, 1.989, 1.966, 1.955, + 1.928, 1.944, 1.973, 1.999, 2.007, 2.016, 2.019, 2.025, 2.026, 2.025, 2.017, 2.008, 1.997, 1.975, 1.958, 1.947 + ] + } + ], + "luminance_lut": + [ + 1.877, 1.597, 1.397, 1.269, 1.191, 1.131, 1.093, 1.078, 1.071, 1.069, 1.086, 1.135, 1.221, 1.331, 1.474, 1.704, + 1.749, 1.506, 1.334, 1.229, 1.149, 1.088, 1.058, 1.053, 1.051, 1.046, 1.053, 1.091, 1.163, 1.259, 1.387, 1.587, + 1.661, 1.451, 1.295, 1.195, 1.113, 1.061, 1.049, 1.048, 1.047, 1.049, 1.049, 1.066, 1.124, 1.211, 1.333, 1.511, + 1.615, 1.411, 1.267, 1.165, 1.086, 1.052, 1.047, 1.047, 1.047, 1.049, 1.052, 1.056, 1.099, 1.181, 1.303, 1.471, + 1.576, 1.385, 1.252, 1.144, 1.068, 1.049, 1.044, 1.044, 1.045, 1.049, 1.053, 1.054, 1.083, 1.163, 1.283, 1.447, + 1.561, 1.373, 1.245, 1.135, 1.064, 1.049, 1.044, 1.044, 1.044, 1.046, 1.048, 1.054, 1.073, 1.153, 1.271, 1.432, + 1.571, 1.377, 1.242, 1.137, 1.066, 1.055, 1.052, 1.051, 1.051, 1.049, 1.047, 1.048, 1.068, 1.148, 1.271, 1.427, + 1.582, 1.396, 1.259, 1.156, 1.085, 1.068, 1.059, 1.054, 1.049, 1.045, 1.041, 1.043, 1.074, 1.157, 1.284, 1.444, + 1.623, 1.428, 1.283, 1.178, 1.105, 1.074, 1.069, 1.063, 1.056, 1.048, 1.046, 1.051, 1.094, 1.182, 1.311, 1.473, + 1.691, 1.471, 1.321, 1.213, 1.135, 1.088, 1.073, 1.069, 1.063, 1.059, 1.053, 1.071, 1.129, 1.222, 1.351, 1.521, + 1.808, 1.543, 1.371, 1.253, 1.174, 1.118, 1.085, 1.072, 1.067, 1.064, 1.071, 1.106, 1.176, 1.274, 1.398, 1.582, + 1.969, 1.666, 1.447, 1.316, 1.223, 1.166, 1.123, 1.094, 1.089, 1.097, 1.118, 1.163, 1.239, 1.336, 1.471, 1.681 + ], + "sigma": 0.00218, + "sigma_Cb": 0.00194 + } + }, + { + "rpi.contrast": + { + "ce_enable": 1, + "gamma_curve": + [ + 0, 0, + 1024, 5040, + 2048, 9338, + 3072, 12356, + 4096, 15312, + 5120, 18051, + 6144, 20790, + 7168, 23193, + 8192, 25744, + 9216, 27942, + 10240, 30035, + 11264, 32005, + 12288, 33975, + 13312, 35815, + 14336, 37600, + 15360, 39168, + 16384, 40642, + 18432, 43379, + 20480, 45749, + 22528, 47753, + 24576, 49621, + 26624, 51253, + 28672, 52698, + 30720, 53796, + 32768, 54876, + 36864, 57012, + 40960, 58656, + 45056, 59954, + 49152, 61183, + 53248, 62355, + 57344, 63419, + 61440, 64476, + 65535, 65535 + ] + } + }, + { + "rpi.ccm": + { + "ccms": [ + { + "ct": 2900, + "ccm": + [ + 1.44924, -0.12935, -0.31989, + -0.65839, 1.95441, -0.29602, + 0.18344, -1.22282, 2.03938 + ] + }, + { + "ct": 3000, + "ccm": + [ + 1.38736, 0.07714, -0.46451, + -0.59691, 1.84335, -0.24644, + 0.10092, -1.30441, 2.20349 + ] + }, + { + "ct": 3600, + "ccm": + [ + 1.51261, -0.27921, -0.23339, + -0.55129, 1.83241, -0.28111, + 0.11649, -0.93195, 1.81546 + ] + }, + { + "ct": 4600, + "ccm": + [ + 1.47082, -0.18523, -0.28559, + -0.48923, 1.95126, -0.46203, + 0.07951, -0.83987, 1.76036 + ] + }, + { + "ct": 5800, + "ccm": + [ + 1.57294, -0.36229, -0.21065, + -0.42272, 1.80305, -0.38032, + 0.03671, -0.66862, 1.63191 + ] + }, + { + "ct": 8100, + "ccm": + [ + 1.58803, -0.09912, -0.48891, + -0.42594, 2.22303, -0.79709, + -0.00621, -0.90516, 1.91137 + ] + } + ] + } + }, + { + "rpi.sharpen": + { + "threshold": 2.0, + "strength": 0.5, + "limit": 0.5 + } + } + ] +} \ No newline at end of file diff --git a/src/ipa/rpi/vc4/data/uncalibrated.json b/src/ipa/rpi/vc4/data/uncalibrated.json new file mode 100644 index 00000000..13eb3f17 --- /dev/null +++ b/src/ipa/rpi/vc4/data/uncalibrated.json @@ -0,0 +1,118 @@ +{ + "version": 2.0, + "target": "bcm2835", + "algorithms": [ + { + "rpi.black_level": + { + "black_level": 4096 + } + }, + { + "rpi.awb": + { + "use_derivatives": 0, + "bayes": 0 + } + }, + { + "rpi.agc": + { + "metering_modes": + { + "centre-weighted": + { + "weights": [ 4, 4, 4, 2, 2, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0 ] + } + }, + "exposure_modes": + { + "normal": + { + "shutter": [ 100, 15000, 30000, 60000, 120000 ], + "gain": [ 1.0, 2.0, 3.0, 4.0, 6.0 ] + } + }, + "constraint_modes": + { + "normal": [ + { + "bound": "LOWER", + "q_lo": 0.98, + "q_hi": 1.0, + "y_target": + [ + 0, 0.4, + 1000, 0.4 + ] + } + ] + }, + "y_target": + [ + 0, 0.16, + 1000, 0.165, + 10000, 0.17 + ] + } + }, + { + "rpi.ccm": + { + "ccms": [ + { + "ct": 4000, + "ccm": + [ + 2.0, -1.0, 0.0, + -0.5, 2.0, -0.5, + 0, -1.0, 2.0 + ] + } + ] + } + }, + { + "rpi.contrast": + { + "ce_enable": 0, + "gamma_curve": + [ + 0, 0, + 1024, 5040, + 2048, 9338, + 3072, 12356, + 4096, 15312, + 5120, 18051, + 6144, 20790, + 7168, 23193, + 8192, 25744, + 9216, 27942, + 10240, 30035, + 11264, 32005, + 12288, 33975, + 13312, 35815, + 14336, 37600, + 15360, 39168, + 16384, 40642, + 18432, 43379, + 20480, 45749, + 22528, 47753, + 24576, 49621, + 26624, 51253, + 28672, 52698, + 30720, 53796, + 32768, 54876, + 36864, 57012, + 40960, 58656, + 45056, 59954, + 49152, 61183, + 53248, 62355, + 57344, 63419, + 61440, 64476, + 65535, 65535 + ] + } + } + ] +} \ No newline at end of file diff --git a/src/ipa/rpi/vc4/meson.build b/src/ipa/rpi/vc4/meson.build new file mode 100644 index 00000000..df01c150 --- /dev/null +++ b/src/ipa/rpi/vc4/meson.build @@ -0,0 +1,47 @@ +# SPDX-License-Identifier: CC0-1.0 + +ipa_name = 'ipa_rpi_vc4' + +vc4_ipa_deps = [ + libcamera_private, + libatomic, +] + +vc4_ipa_libs = [ + rpi_ipa_cam_helper_lib, + rpi_ipa_controller_lib +] + +vc4_ipa_includes = [ + ipa_includes, + libipa_includes, +] + +vc4_ipa_sources = files([ + 'raspberrypi.cpp', +]) + +vc4_ipa_includes += include_directories('..') + +mod = shared_module(ipa_name, + [vc4_ipa_sources, libcamera_generated_ipa_headers], + name_prefix : '', + include_directories : vc4_ipa_includes, + dependencies : vc4_ipa_deps, + link_with : libipa, + link_whole : vc4_ipa_libs, + install : true, + install_dir : ipa_install_dir) + +if ipa_sign_module + custom_target(ipa_name + '.so.sign', + input : mod, + output : ipa_name + '.so.sign', + command : [ipa_sign, ipa_priv_key, '@INPUT@', '@OUTPUT@'], + install : false, + build_by_default : true) +endif + +subdir('data') + +ipa_names += ipa_name diff --git a/src/ipa/rpi/vc4/raspberrypi.cpp b/src/ipa/rpi/vc4/raspberrypi.cpp new file mode 100644 index 00000000..5d3bf4ca --- /dev/null +++ b/src/ipa/rpi/vc4/raspberrypi.cpp @@ -0,0 +1,1853 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (C) 2019-2021, Raspberry Pi Ltd + * + * rpi.cpp - Raspberry Pi Image Processing Algorithms + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include "libcamera/internal/mapped_framebuffer.h" + +#include "cam_helper/cam_helper.h" +#include "controller/af_algorithm.h" +#include "controller/af_status.h" +#include "controller/agc_algorithm.h" +#include "controller/agc_status.h" +#include "controller/alsc_status.h" +#include "controller/awb_algorithm.h" +#include "controller/awb_status.h" +#include "controller/black_level_status.h" +#include "controller/ccm_algorithm.h" +#include "controller/ccm_status.h" +#include "controller/contrast_algorithm.h" +#include "controller/contrast_status.h" +#include "controller/controller.h" +#include "controller/denoise_algorithm.h" +#include "controller/denoise_status.h" +#include "controller/dpc_status.h" +#include "controller/geq_status.h" +#include "controller/lux_status.h" +#include "controller/metadata.h" +#include "controller/sharpen_algorithm.h" +#include "controller/sharpen_status.h" +#include "controller/statistics.h" + +namespace libcamera { + +using namespace std::literals::chrono_literals; +using utils::Duration; + +/* Number of metadata objects available in the context list. */ +constexpr unsigned int numMetadataContexts = 16; + +/* Number of frame length times to hold in the queue. */ +constexpr unsigned int FrameLengthsQueueSize = 10; + +/* Configure the sensor with these values initially. */ +constexpr double defaultAnalogueGain = 1.0; +constexpr Duration defaultExposureTime = 20.0ms; +constexpr Duration defaultMinFrameDuration = 1.0s / 30.0; +constexpr Duration defaultMaxFrameDuration = 250.0s; + +/* + * Determine the minimum allowable inter-frame duration to run the controller + * algorithms. If the pipeline handler provider frames at a rate higher than this, + * we rate-limit the controller Prepare() and Process() calls to lower than or + * equal to this rate. + */ +constexpr Duration controllerMinFrameDuration = 1.0s / 30.0; + +/* List of controls handled by the Raspberry Pi IPA */ +static const ControlInfoMap::Map ipaControls{ + { &controls::AeEnable, ControlInfo(false, true) }, + { &controls::ExposureTime, ControlInfo(0, 66666) }, + { &controls::AnalogueGain, ControlInfo(1.0f, 16.0f) }, + { &controls::AeMeteringMode, ControlInfo(controls::AeMeteringModeValues) }, + { &controls::AeConstraintMode, ControlInfo(controls::AeConstraintModeValues) }, + { &controls::AeExposureMode, ControlInfo(controls::AeExposureModeValues) }, + { &controls::ExposureValue, ControlInfo(-8.0f, 8.0f, 0.0f) }, + { &controls::AwbEnable, ControlInfo(false, true) }, + { &controls::ColourGains, ControlInfo(0.0f, 32.0f) }, + { &controls::AwbMode, ControlInfo(controls::AwbModeValues) }, + { &controls::Brightness, ControlInfo(-1.0f, 1.0f, 0.0f) }, + { &controls::Contrast, ControlInfo(0.0f, 32.0f, 1.0f) }, + { &controls::Saturation, ControlInfo(0.0f, 32.0f, 1.0f) }, + { &controls::Sharpness, ControlInfo(0.0f, 16.0f, 1.0f) }, + { &controls::ColourCorrectionMatrix, ControlInfo(-16.0f, 16.0f) }, + { &controls::ScalerCrop, ControlInfo(Rectangle{}, Rectangle(65535, 65535, 65535, 65535), Rectangle{}) }, + { &controls::FrameDurationLimits, ControlInfo(INT64_C(33333), INT64_C(120000)) }, + { &controls::draft::NoiseReductionMode, ControlInfo(controls::draft::NoiseReductionModeValues) } +}; + +/* IPA controls handled conditionally, if the lens has a focus control */ +static const ControlInfoMap::Map ipaAfControls{ + { &controls::AfMode, ControlInfo(controls::AfModeValues) }, + { &controls::AfRange, ControlInfo(controls::AfRangeValues) }, + { &controls::AfSpeed, ControlInfo(controls::AfSpeedValues) }, + { &controls::AfMetering, ControlInfo(controls::AfMeteringValues) }, + { &controls::AfWindows, ControlInfo(Rectangle{}, Rectangle(65535, 65535, 65535, 65535), Rectangle{}) }, + { &controls::AfTrigger, ControlInfo(controls::AfTriggerValues) }, + { &controls::AfPause, ControlInfo(controls::AfPauseValues) }, + { &controls::LensPosition, ControlInfo(0.0f, 32.0f, 1.0f) } +}; + +LOG_DEFINE_CATEGORY(IPARPI) + +namespace ipa::RPi { + +class IPARPi : public IPARPiInterface +{ +public: + IPARPi() + : controller_(), frameCount_(0), checkCount_(0), mistrustCount_(0), + lastRunTimestamp_(0), lsTable_(nullptr), firstStart_(true), + lastTimeout_(0s) + { + } + + ~IPARPi() + { + if (lsTable_) + munmap(lsTable_, MaxLsGridSize); + } + + int init(const IPASettings &settings, bool lensPresent, IPAInitResult *result) override; + void start(const ControlList &controls, StartConfig *startConfig) override; + void stop() override {} + + int configure(const IPACameraSensorInfo &sensorInfo, const IPAConfig &data, + ControlList *controls, IPAConfigResult *result) override; + void mapBuffers(const std::vector &buffers) override; + void unmapBuffers(const std::vector &ids) override; + void signalStatReady(const uint32_t bufferId, uint32_t ipaContext) override; + void signalQueueRequest(const ControlList &controls) override; + void signalIspPrepare(const ISPConfig &data) override; + +private: + void setMode(const IPACameraSensorInfo &sensorInfo); + bool validateSensorControls(); + bool validateIspControls(); + bool validateLensControls(); + void queueRequest(const ControlList &controls); + void returnEmbeddedBuffer(unsigned int bufferId); + void prepareISP(const ISPConfig &data); + void reportMetadata(unsigned int ipaContext); + void fillDeviceStatus(const ControlList &sensorControls, unsigned int ipaContext); + RPiController::StatisticsPtr fillStatistics(bcm2835_isp_stats *stats) const; + void processStats(unsigned int bufferId, unsigned int ipaContext); + void setCameraTimeoutValue(); + void applyFrameDurations(Duration minFrameDuration, Duration maxFrameDuration); + void applyAGC(const struct AgcStatus *agcStatus, ControlList &ctrls); + void applyAWB(const struct AwbStatus *awbStatus, ControlList &ctrls); + void applyDG(const struct AgcStatus *dgStatus, ControlList &ctrls); + void applyCCM(const struct CcmStatus *ccmStatus, ControlList &ctrls); + void applyBlackLevel(const struct BlackLevelStatus *blackLevelStatus, ControlList &ctrls); + void applyGamma(const struct ContrastStatus *contrastStatus, ControlList &ctrls); + void applyGEQ(const struct GeqStatus *geqStatus, ControlList &ctrls); + void applyDenoise(const struct DenoiseStatus *denoiseStatus, ControlList &ctrls); + void applySharpen(const struct SharpenStatus *sharpenStatus, ControlList &ctrls); + void applyDPC(const struct DpcStatus *dpcStatus, ControlList &ctrls); + void applyLS(const struct AlscStatus *lsStatus, ControlList &ctrls); + void applyAF(const struct AfStatus *afStatus, ControlList &lensCtrls); + void resampleTable(uint16_t dest[], const std::vector &src, int destW, int destH); + + std::map buffers_; + + ControlInfoMap sensorCtrls_; + ControlInfoMap ispCtrls_; + ControlInfoMap lensCtrls_; + bool lensPresent_; + ControlList libcameraMetadata_; + + /* Camera sensor params. */ + CameraMode mode_; + + /* Raspberry Pi controller specific defines. */ + std::unique_ptr helper_; + RPiController::Controller controller_; + std::array rpiMetadata_; + + /* + * We count frames to decide if the frame must be hidden (e.g. from + * display) or mistrusted (i.e. not given to the control algos). + */ + uint64_t frameCount_; + + /* For checking the sequencing of Prepare/Process calls. */ + uint64_t checkCount_; + + /* How many frames we should avoid running control algos on. */ + unsigned int mistrustCount_; + + /* Number of frames that need to be dropped on startup. */ + unsigned int dropFrameCount_; + + /* Frame timestamp for the last run of the controller. */ + uint64_t lastRunTimestamp_; + + /* Do we run a Controller::process() for this frame? */ + bool processPending_; + + /* LS table allocation passed in from the pipeline handler. */ + SharedFD lsTableHandle_; + void *lsTable_; + + /* Distinguish the first camera start from others. */ + bool firstStart_; + + /* Frame duration (1/fps) limits. */ + Duration minFrameDuration_; + Duration maxFrameDuration_; + + /* Track the frame length times over FrameLengthsQueueSize frames. */ + std::deque frameLengths_; + Duration lastTimeout_; +}; + +int IPARPi::init(const IPASettings &settings, bool lensPresent, IPAInitResult *result) +{ + /* + * Load the "helper" for this sensor. This tells us all the device specific stuff + * that the kernel driver doesn't. We only do this the first time; we don't need + * to re-parse the metadata after a simple mode-switch for no reason. + */ + helper_ = std::unique_ptr(RPiController::CamHelper::create(settings.sensorModel)); + if (!helper_) { + LOG(IPARPI, Error) << "Could not create camera helper for " + << settings.sensorModel; + return -EINVAL; + } + + /* + * Pass out the sensor config to the pipeline handler in order + * to setup the staggered writer class. + */ + int gainDelay, exposureDelay, vblankDelay, hblankDelay, sensorMetadata; + helper_->getDelays(exposureDelay, gainDelay, vblankDelay, hblankDelay); + sensorMetadata = helper_->sensorEmbeddedDataPresent(); + + result->sensorConfig.gainDelay = gainDelay; + result->sensorConfig.exposureDelay = exposureDelay; + result->sensorConfig.vblankDelay = vblankDelay; + result->sensorConfig.hblankDelay = hblankDelay; + result->sensorConfig.sensorMetadata = sensorMetadata; + + /* Load the tuning file for this sensor. */ + int ret = controller_.read(settings.configurationFile.c_str()); + if (ret) { + LOG(IPARPI, Error) + << "Failed to load tuning data file " + << settings.configurationFile; + return ret; + } + + const std::string &target = controller_.getTarget(); + if (target != "bcm2835") { + LOG(IPARPI, Error) + << "Tuning data file target returned \"" << target << "\"" + << ", expected \"bcm2835\""; + return -EINVAL; + } + + lensPresent_ = lensPresent; + + controller_.initialise(); + + /* Return the controls handled by the IPA */ + ControlInfoMap::Map ctrlMap = ipaControls; + if (lensPresent_) + ctrlMap.merge(ControlInfoMap::Map(ipaAfControls)); + result->controlInfo = ControlInfoMap(std::move(ctrlMap), controls::controls); + + return 0; +} + +void IPARPi::start(const ControlList &controls, StartConfig *startConfig) +{ + RPiController::Metadata metadata; + + ASSERT(startConfig); + if (!controls.empty()) { + /* We have been given some controls to action before start. */ + queueRequest(controls); + } + + controller_.switchMode(mode_, &metadata); + + /* Reset the frame lengths queue state. */ + lastTimeout_ = 0s; + frameLengths_.clear(); + frameLengths_.resize(FrameLengthsQueueSize, 0s); + + /* SwitchMode may supply updated exposure/gain values to use. */ + AgcStatus agcStatus; + agcStatus.shutterTime = 0.0s; + agcStatus.analogueGain = 0.0; + + metadata.get("agc.status", agcStatus); + if (agcStatus.shutterTime && agcStatus.analogueGain) { + ControlList ctrls(sensorCtrls_); + applyAGC(&agcStatus, ctrls); + startConfig->controls = std::move(ctrls); + setCameraTimeoutValue(); + } + + /* + * Initialise frame counts, and decide how many frames must be hidden or + * "mistrusted", which depends on whether this is a startup from cold, + * or merely a mode switch in a running system. + */ + frameCount_ = 0; + checkCount_ = 0; + if (firstStart_) { + dropFrameCount_ = helper_->hideFramesStartup(); + mistrustCount_ = helper_->mistrustFramesStartup(); + + /* + * Query the AGC/AWB for how many frames they may take to + * converge sufficiently. Where these numbers are non-zero + * we must allow for the frames with bad statistics + * (mistrustCount_) that they won't see. But if zero (i.e. + * no convergence necessary), no frames need to be dropped. + */ + unsigned int agcConvergenceFrames = 0; + RPiController::AgcAlgorithm *agc = dynamic_cast( + controller_.getAlgorithm("agc")); + if (agc) { + agcConvergenceFrames = agc->getConvergenceFrames(); + if (agcConvergenceFrames) + agcConvergenceFrames += mistrustCount_; + } + + unsigned int awbConvergenceFrames = 0; + RPiController::AwbAlgorithm *awb = dynamic_cast( + controller_.getAlgorithm("awb")); + if (awb) { + awbConvergenceFrames = awb->getConvergenceFrames(); + if (awbConvergenceFrames) + awbConvergenceFrames += mistrustCount_; + } + + dropFrameCount_ = std::max({ dropFrameCount_, agcConvergenceFrames, awbConvergenceFrames }); + LOG(IPARPI, Debug) << "Drop " << dropFrameCount_ << " frames on startup"; + } else { + dropFrameCount_ = helper_->hideFramesModeSwitch(); + mistrustCount_ = helper_->mistrustFramesModeSwitch(); + } + + startConfig->dropFrameCount = dropFrameCount_; + + firstStart_ = false; + lastRunTimestamp_ = 0; +} + +void IPARPi::setMode(const IPACameraSensorInfo &sensorInfo) +{ + mode_.bitdepth = sensorInfo.bitsPerPixel; + mode_.width = sensorInfo.outputSize.width; + mode_.height = sensorInfo.outputSize.height; + mode_.sensorWidth = sensorInfo.activeAreaSize.width; + mode_.sensorHeight = sensorInfo.activeAreaSize.height; + mode_.cropX = sensorInfo.analogCrop.x; + mode_.cropY = sensorInfo.analogCrop.y; + mode_.pixelRate = sensorInfo.pixelRate; + + /* + * Calculate scaling parameters. The scale_[xy] factors are determined + * by the ratio between the crop rectangle size and the output size. + */ + mode_.scaleX = sensorInfo.analogCrop.width / sensorInfo.outputSize.width; + mode_.scaleY = sensorInfo.analogCrop.height / sensorInfo.outputSize.height; + + /* + * We're not told by the pipeline handler how scaling is split between + * binning and digital scaling. For now, as a heuristic, assume that + * downscaling up to 2 is achieved through binning, and that any + * additional scaling is achieved through digital scaling. + * + * \todo Get the pipeline handle to provide the full data + */ + mode_.binX = std::min(2, static_cast(mode_.scaleX)); + mode_.binY = std::min(2, static_cast(mode_.scaleY)); + + /* The noise factor is the square root of the total binning factor. */ + mode_.noiseFactor = sqrt(mode_.binX * mode_.binY); + + /* + * Calculate the line length as the ratio between the line length in + * pixels and the pixel rate. + */ + mode_.minLineLength = sensorInfo.minLineLength * (1.0s / sensorInfo.pixelRate); + mode_.maxLineLength = sensorInfo.maxLineLength * (1.0s / sensorInfo.pixelRate); + + /* + * Set the frame length limits for the mode to ensure exposure and + * framerate calculations are clipped appropriately. + */ + mode_.minFrameLength = sensorInfo.minFrameLength; + mode_.maxFrameLength = sensorInfo.maxFrameLength; + + /* Store these for convenience. */ + mode_.minFrameDuration = mode_.minFrameLength * mode_.minLineLength; + mode_.maxFrameDuration = mode_.maxFrameLength * mode_.maxLineLength; + + /* + * Some sensors may have different sensitivities in different modes; + * the CamHelper will know the correct value. + */ + mode_.sensitivity = helper_->getModeSensitivity(mode_); + + const ControlInfo &gainCtrl = sensorCtrls_.at(V4L2_CID_ANALOGUE_GAIN); + const ControlInfo &shutterCtrl = sensorCtrls_.at(V4L2_CID_EXPOSURE); + + mode_.minAnalogueGain = helper_->gain(gainCtrl.min().get()); + mode_.maxAnalogueGain = helper_->gain(gainCtrl.max().get()); + + /* Shutter speed is calculated based on the limits of the frame durations. */ + mode_.minShutter = helper_->exposure(shutterCtrl.min().get(), mode_.minLineLength); + mode_.maxShutter = Duration::max(); + helper_->getBlanking(mode_.maxShutter, + mode_.minFrameDuration, mode_.maxFrameDuration); +} + +int IPARPi::configure(const IPACameraSensorInfo &sensorInfo, const IPAConfig &ipaConfig, + ControlList *controls, IPAConfigResult *result) +{ + sensorCtrls_ = ipaConfig.sensorControls; + ispCtrls_ = ipaConfig.ispControls; + + if (!validateSensorControls()) { + LOG(IPARPI, Error) << "Sensor control validation failed."; + return -1; + } + + if (!validateIspControls()) { + LOG(IPARPI, Error) << "ISP control validation failed."; + return -1; + } + + if (lensPresent_) { + lensCtrls_ = ipaConfig.lensControls; + if (!validateLensControls()) { + LOG(IPARPI, Warning) << "Lens validation failed, " + << "no lens control will be available."; + lensPresent_ = false; + } + } + + /* Setup a metadata ControlList to output metadata. */ + libcameraMetadata_ = ControlList(controls::controls); + + /* Re-assemble camera mode using the sensor info. */ + setMode(sensorInfo); + + mode_.transform = static_cast(ipaConfig.transform); + + /* Store the lens shading table pointer and handle if available. */ + if (ipaConfig.lsTableHandle.isValid()) { + /* Remove any previous table, if there was one. */ + if (lsTable_) { + munmap(lsTable_, MaxLsGridSize); + lsTable_ = nullptr; + } + + /* Map the LS table buffer into user space. */ + lsTableHandle_ = std::move(ipaConfig.lsTableHandle); + if (lsTableHandle_.isValid()) { + lsTable_ = mmap(nullptr, MaxLsGridSize, PROT_READ | PROT_WRITE, + MAP_SHARED, lsTableHandle_.get(), 0); + + if (lsTable_ == MAP_FAILED) { + LOG(IPARPI, Error) << "dmaHeap mmap failure for LS table."; + lsTable_ = nullptr; + } + } + } + + /* Pass the camera mode to the CamHelper to setup algorithms. */ + helper_->setCameraMode(mode_); + + /* + * Initialise this ControlList correctly, even if empty, in case the IPA is + * running is isolation mode (passing the ControlList through the IPC layer). + */ + ControlList ctrls(sensorCtrls_); + + /* The pipeline handler passes out the mode's sensitivity. */ + result->modeSensitivity = mode_.sensitivity; + + if (firstStart_) { + /* Supply initial values for frame durations. */ + applyFrameDurations(defaultMinFrameDuration, defaultMaxFrameDuration); + + /* Supply initial values for gain and exposure. */ + AgcStatus agcStatus; + agcStatus.shutterTime = defaultExposureTime; + agcStatus.analogueGain = defaultAnalogueGain; + applyAGC(&agcStatus, ctrls); + } + + ASSERT(controls); + *controls = std::move(ctrls); + + /* + * Apply the correct limits to the exposure, gain and frame duration controls + * based on the current sensor mode. + */ + ControlInfoMap::Map ctrlMap = ipaControls; + ctrlMap[&controls::FrameDurationLimits] = + ControlInfo(static_cast(mode_.minFrameDuration.get()), + static_cast(mode_.maxFrameDuration.get())); + + ctrlMap[&controls::AnalogueGain] = + ControlInfo(static_cast(mode_.minAnalogueGain), + static_cast(mode_.maxAnalogueGain)); + + ctrlMap[&controls::ExposureTime] = + ControlInfo(static_cast(mode_.minShutter.get()), + static_cast(mode_.maxShutter.get())); + + /* Declare Autofocus controls, only if we have a controllable lens */ + if (lensPresent_) + ctrlMap.merge(ControlInfoMap::Map(ipaAfControls)); + + result->controlInfo = ControlInfoMap(std::move(ctrlMap), controls::controls); + return 0; +} + +void IPARPi::mapBuffers(const std::vector &buffers) +{ + for (const IPABuffer &buffer : buffers) { + const FrameBuffer fb(buffer.planes); + buffers_.emplace(buffer.id, + MappedFrameBuffer(&fb, MappedFrameBuffer::MapFlag::ReadWrite)); + } +} + +void IPARPi::unmapBuffers(const std::vector &ids) +{ + for (unsigned int id : ids) { + auto it = buffers_.find(id); + if (it == buffers_.end()) + continue; + + buffers_.erase(id); + } +} + +void IPARPi::signalStatReady(uint32_t bufferId, uint32_t ipaContext) +{ + unsigned int context = ipaContext % rpiMetadata_.size(); + + if (++checkCount_ != frameCount_) /* assert here? */ + LOG(IPARPI, Error) << "WARNING: Prepare/Process mismatch!!!"; + if (processPending_ && frameCount_ > mistrustCount_) + processStats(bufferId, context); + + reportMetadata(context); + + statsMetadataComplete.emit(bufferId, libcameraMetadata_); +} + +void IPARPi::signalQueueRequest(const ControlList &controls) +{ + queueRequest(controls); +} + +void IPARPi::signalIspPrepare(const ISPConfig &data) +{ + /* + * At start-up, or after a mode-switch, we may want to + * avoid running the control algos for a few frames in case + * they are "unreliable". + */ + prepareISP(data); + frameCount_++; + + /* Ready to push the input buffer into the ISP. */ + runIsp.emit(data.bayerBufferId); +} + +void IPARPi::reportMetadata(unsigned int ipaContext) +{ + RPiController::Metadata &rpiMetadata = rpiMetadata_[ipaContext]; + std::unique_lock lock(rpiMetadata); + + /* + * Certain information about the current frame and how it will be + * processed can be extracted and placed into the libcamera metadata + * buffer, where an application could query it. + */ + DeviceStatus *deviceStatus = rpiMetadata.getLocked("device.status"); + if (deviceStatus) { + libcameraMetadata_.set(controls::ExposureTime, + deviceStatus->shutterSpeed.get()); + libcameraMetadata_.set(controls::AnalogueGain, deviceStatus->analogueGain); + libcameraMetadata_.set(controls::FrameDuration, + helper_->exposure(deviceStatus->frameLength, deviceStatus->lineLength).get()); + if (deviceStatus->sensorTemperature) + libcameraMetadata_.set(controls::SensorTemperature, *deviceStatus->sensorTemperature); + if (deviceStatus->lensPosition) + libcameraMetadata_.set(controls::LensPosition, *deviceStatus->lensPosition); + } + + AgcStatus *agcStatus = rpiMetadata.getLocked("agc.status"); + if (agcStatus) { + libcameraMetadata_.set(controls::AeLocked, agcStatus->locked); + libcameraMetadata_.set(controls::DigitalGain, agcStatus->digitalGain); + } + + LuxStatus *luxStatus = rpiMetadata.getLocked("lux.status"); + if (luxStatus) + libcameraMetadata_.set(controls::Lux, luxStatus->lux); + + AwbStatus *awbStatus = rpiMetadata.getLocked("awb.status"); + if (awbStatus) { + libcameraMetadata_.set(controls::ColourGains, { static_cast(awbStatus->gainR), + static_cast(awbStatus->gainB) }); + libcameraMetadata_.set(controls::ColourTemperature, awbStatus->temperatureK); + } + + BlackLevelStatus *blackLevelStatus = rpiMetadata.getLocked("black_level.status"); + if (blackLevelStatus) + libcameraMetadata_.set(controls::SensorBlackLevels, + { static_cast(blackLevelStatus->blackLevelR), + static_cast(blackLevelStatus->blackLevelG), + static_cast(blackLevelStatus->blackLevelG), + static_cast(blackLevelStatus->blackLevelB) }); + + RPiController::FocusRegions *focusStatus = + rpiMetadata.getLocked("focus.status"); + if (focusStatus) { + /* + * Calculate the average FoM over the central (symmetric) positions + * to give an overall scene FoM. This can change later if it is + * not deemed suitable. + */ + libcamera::Size size = focusStatus->size(); + unsigned rows = size.height; + unsigned cols = size.width; + + uint64_t sum = 0; + unsigned int numRegions = 0; + for (unsigned r = rows / 3; r < rows - rows / 3; ++r) { + for (unsigned c = cols / 4; c < cols - cols / 4; ++c) { + sum += focusStatus->get({ (int)c, (int)r }).val; + numRegions++; + } + } + + uint32_t focusFoM = (sum / numRegions) >> 16; + libcameraMetadata_.set(controls::FocusFoM, focusFoM); + } + + CcmStatus *ccmStatus = rpiMetadata.getLocked("ccm.status"); + if (ccmStatus) { + float m[9]; + for (unsigned int i = 0; i < 9; i++) + m[i] = ccmStatus->matrix[i]; + libcameraMetadata_.set(controls::ColourCorrectionMatrix, m); + } + + const AfStatus *afStatus = rpiMetadata.getLocked("af.status"); + if (afStatus) { + int32_t s, p; + switch (afStatus->state) { + case AfState::Scanning: + s = controls::AfStateScanning; + break; + case AfState::Focused: + s = controls::AfStateFocused; + break; + case AfState::Failed: + s = controls::AfStateFailed; + break; + default: + s = controls::AfStateIdle; + } + switch (afStatus->pauseState) { + case AfPauseState::Pausing: + p = controls::AfPauseStatePausing; + break; + case AfPauseState::Paused: + p = controls::AfPauseStatePaused; + break; + default: + p = controls::AfPauseStateRunning; + } + libcameraMetadata_.set(controls::AfState, s); + libcameraMetadata_.set(controls::AfPauseState, p); + } +} + +bool IPARPi::validateSensorControls() +{ + static const uint32_t ctrls[] = { + V4L2_CID_ANALOGUE_GAIN, + V4L2_CID_EXPOSURE, + V4L2_CID_VBLANK, + V4L2_CID_HBLANK, + }; + + for (auto c : ctrls) { + if (sensorCtrls_.find(c) == sensorCtrls_.end()) { + LOG(IPARPI, Error) << "Unable to find sensor control " + << utils::hex(c); + return false; + } + } + + return true; +} + +bool IPARPi::validateIspControls() +{ + static const uint32_t ctrls[] = { + V4L2_CID_RED_BALANCE, + V4L2_CID_BLUE_BALANCE, + V4L2_CID_DIGITAL_GAIN, + V4L2_CID_USER_BCM2835_ISP_CC_MATRIX, + V4L2_CID_USER_BCM2835_ISP_GAMMA, + V4L2_CID_USER_BCM2835_ISP_BLACK_LEVEL, + V4L2_CID_USER_BCM2835_ISP_GEQ, + V4L2_CID_USER_BCM2835_ISP_DENOISE, + V4L2_CID_USER_BCM2835_ISP_SHARPEN, + V4L2_CID_USER_BCM2835_ISP_DPC, + V4L2_CID_USER_BCM2835_ISP_LENS_SHADING, + V4L2_CID_USER_BCM2835_ISP_CDN, + }; + + for (auto c : ctrls) { + if (ispCtrls_.find(c) == ispCtrls_.end()) { + LOG(IPARPI, Error) << "Unable to find ISP control " + << utils::hex(c); + return false; + } + } + + return true; +} + +bool IPARPi::validateLensControls() +{ + if (lensCtrls_.find(V4L2_CID_FOCUS_ABSOLUTE) == lensCtrls_.end()) { + LOG(IPARPI, Error) << "Unable to find Lens control V4L2_CID_FOCUS_ABSOLUTE"; + return false; + } + + return true; +} + +/* + * Converting between enums (used in the libcamera API) and the names that + * we use to identify different modes. Unfortunately, the conversion tables + * must be kept up-to-date by hand. + */ +static const std::map MeteringModeTable = { + { controls::MeteringCentreWeighted, "centre-weighted" }, + { controls::MeteringSpot, "spot" }, + { controls::MeteringMatrix, "matrix" }, + { controls::MeteringCustom, "custom" }, +}; + +static const std::map ConstraintModeTable = { + { controls::ConstraintNormal, "normal" }, + { controls::ConstraintHighlight, "highlight" }, + { controls::ConstraintShadows, "shadows" }, + { controls::ConstraintCustom, "custom" }, +}; + +static const std::map ExposureModeTable = { + { controls::ExposureNormal, "normal" }, + { controls::ExposureShort, "short" }, + { controls::ExposureLong, "long" }, + { controls::ExposureCustom, "custom" }, +}; + +static const std::map AwbModeTable = { + { controls::AwbAuto, "auto" }, + { controls::AwbIncandescent, "incandescent" }, + { controls::AwbTungsten, "tungsten" }, + { controls::AwbFluorescent, "fluorescent" }, + { controls::AwbIndoor, "indoor" }, + { controls::AwbDaylight, "daylight" }, + { controls::AwbCloudy, "cloudy" }, + { controls::AwbCustom, "custom" }, +}; + +static const std::map DenoiseModeTable = { + { controls::draft::NoiseReductionModeOff, RPiController::DenoiseMode::Off }, + { controls::draft::NoiseReductionModeFast, RPiController::DenoiseMode::ColourFast }, + { controls::draft::NoiseReductionModeHighQuality, RPiController::DenoiseMode::ColourHighQuality }, + { controls::draft::NoiseReductionModeMinimal, RPiController::DenoiseMode::ColourOff }, + { controls::draft::NoiseReductionModeZSL, RPiController::DenoiseMode::ColourHighQuality }, +}; + +static const std::map AfModeTable = { + { controls::AfModeManual, RPiController::AfAlgorithm::AfModeManual }, + { controls::AfModeAuto, RPiController::AfAlgorithm::AfModeAuto }, + { controls::AfModeContinuous, RPiController::AfAlgorithm::AfModeContinuous }, +}; + +static const std::map AfRangeTable = { + { controls::AfRangeNormal, RPiController::AfAlgorithm::AfRangeNormal }, + { controls::AfRangeMacro, RPiController::AfAlgorithm::AfRangeMacro }, + { controls::AfRangeFull, RPiController::AfAlgorithm::AfRangeFull }, +}; + +static const std::map AfPauseTable = { + { controls::AfPauseImmediate, RPiController::AfAlgorithm::AfPauseImmediate }, + { controls::AfPauseDeferred, RPiController::AfAlgorithm::AfPauseDeferred }, + { controls::AfPauseResume, RPiController::AfAlgorithm::AfPauseResume }, +}; + +void IPARPi::queueRequest(const ControlList &controls) +{ + using RPiController::AfAlgorithm; + + /* Clear the return metadata buffer. */ + libcameraMetadata_.clear(); + + /* Because some AF controls are mode-specific, handle AF mode change first. */ + if (controls.contains(controls::AF_MODE)) { + AfAlgorithm *af = dynamic_cast(controller_.getAlgorithm("af")); + if (!af) { + LOG(IPARPI, Warning) + << "Could not set AF_MODE - no AF algorithm"; + } + + int32_t idx = controls.get(controls::AF_MODE).get(); + auto mode = AfModeTable.find(idx); + if (mode == AfModeTable.end()) { + LOG(IPARPI, Error) << "AF mode " << idx + << " not recognised"; + } else + af->setMode(mode->second); + } + + /* Iterate over controls */ + for (auto const &ctrl : controls) { + LOG(IPARPI, Debug) << "Request ctrl: " + << controls::controls.at(ctrl.first)->name() + << " = " << ctrl.second.toString(); + + switch (ctrl.first) { + case controls::AE_ENABLE: { + RPiController::AgcAlgorithm *agc = dynamic_cast( + controller_.getAlgorithm("agc")); + if (!agc) { + LOG(IPARPI, Warning) + << "Could not set AE_ENABLE - no AGC algorithm"; + break; + } + + if (ctrl.second.get() == false) + agc->disableAuto(); + else + agc->enableAuto(); + + libcameraMetadata_.set(controls::AeEnable, ctrl.second.get()); + break; + } + + case controls::EXPOSURE_TIME: { + RPiController::AgcAlgorithm *agc = dynamic_cast( + controller_.getAlgorithm("agc")); + if (!agc) { + LOG(IPARPI, Warning) + << "Could not set EXPOSURE_TIME - no AGC algorithm"; + break; + } + + /* The control provides units of microseconds. */ + agc->setFixedShutter(ctrl.second.get() * 1.0us); + + libcameraMetadata_.set(controls::ExposureTime, ctrl.second.get()); + break; + } + + case controls::ANALOGUE_GAIN: { + RPiController::AgcAlgorithm *agc = dynamic_cast( + controller_.getAlgorithm("agc")); + if (!agc) { + LOG(IPARPI, Warning) + << "Could not set ANALOGUE_GAIN - no AGC algorithm"; + break; + } + + agc->setFixedAnalogueGain(ctrl.second.get()); + + libcameraMetadata_.set(controls::AnalogueGain, + ctrl.second.get()); + break; + } + + case controls::AE_METERING_MODE: { + RPiController::AgcAlgorithm *agc = dynamic_cast( + controller_.getAlgorithm("agc")); + if (!agc) { + LOG(IPARPI, Warning) + << "Could not set AE_METERING_MODE - no AGC algorithm"; + break; + } + + int32_t idx = ctrl.second.get(); + if (MeteringModeTable.count(idx)) { + agc->setMeteringMode(MeteringModeTable.at(idx)); + libcameraMetadata_.set(controls::AeMeteringMode, idx); + } else { + LOG(IPARPI, Error) << "Metering mode " << idx + << " not recognised"; + } + break; + } + + case controls::AE_CONSTRAINT_MODE: { + RPiController::AgcAlgorithm *agc = dynamic_cast( + controller_.getAlgorithm("agc")); + if (!agc) { + LOG(IPARPI, Warning) + << "Could not set AE_CONSTRAINT_MODE - no AGC algorithm"; + break; + } + + int32_t idx = ctrl.second.get(); + if (ConstraintModeTable.count(idx)) { + agc->setConstraintMode(ConstraintModeTable.at(idx)); + libcameraMetadata_.set(controls::AeConstraintMode, idx); + } else { + LOG(IPARPI, Error) << "Constraint mode " << idx + << " not recognised"; + } + break; + } + + case controls::AE_EXPOSURE_MODE: { + RPiController::AgcAlgorithm *agc = dynamic_cast( + controller_.getAlgorithm("agc")); + if (!agc) { + LOG(IPARPI, Warning) + << "Could not set AE_EXPOSURE_MODE - no AGC algorithm"; + break; + } + + int32_t idx = ctrl.second.get(); + if (ExposureModeTable.count(idx)) { + agc->setExposureMode(ExposureModeTable.at(idx)); + libcameraMetadata_.set(controls::AeExposureMode, idx); + } else { + LOG(IPARPI, Error) << "Exposure mode " << idx + << " not recognised"; + } + break; + } + + case controls::EXPOSURE_VALUE: { + RPiController::AgcAlgorithm *agc = dynamic_cast( + controller_.getAlgorithm("agc")); + if (!agc) { + LOG(IPARPI, Warning) + << "Could not set EXPOSURE_VALUE - no AGC algorithm"; + break; + } + + /* + * The SetEv() function takes in a direct exposure multiplier. + * So convert to 2^EV + */ + double ev = pow(2.0, ctrl.second.get()); + agc->setEv(ev); + libcameraMetadata_.set(controls::ExposureValue, + ctrl.second.get()); + break; + } + + case controls::AWB_ENABLE: { + RPiController::AwbAlgorithm *awb = dynamic_cast( + controller_.getAlgorithm("awb")); + if (!awb) { + LOG(IPARPI, Warning) + << "Could not set AWB_ENABLE - no AWB algorithm"; + break; + } + + if (ctrl.second.get() == false) + awb->disableAuto(); + else + awb->enableAuto(); + + libcameraMetadata_.set(controls::AwbEnable, + ctrl.second.get()); + break; + } + + case controls::AWB_MODE: { + RPiController::AwbAlgorithm *awb = dynamic_cast( + controller_.getAlgorithm("awb")); + if (!awb) { + LOG(IPARPI, Warning) + << "Could not set AWB_MODE - no AWB algorithm"; + break; + } + + int32_t idx = ctrl.second.get(); + if (AwbModeTable.count(idx)) { + awb->setMode(AwbModeTable.at(idx)); + libcameraMetadata_.set(controls::AwbMode, idx); + } else { + LOG(IPARPI, Error) << "AWB mode " << idx + << " not recognised"; + } + break; + } + + case controls::COLOUR_GAINS: { + auto gains = ctrl.second.get>(); + RPiController::AwbAlgorithm *awb = dynamic_cast( + controller_.getAlgorithm("awb")); + if (!awb) { + LOG(IPARPI, Warning) + << "Could not set COLOUR_GAINS - no AWB algorithm"; + break; + } + + awb->setManualGains(gains[0], gains[1]); + if (gains[0] != 0.0f && gains[1] != 0.0f) + /* A gain of 0.0f will switch back to auto mode. */ + libcameraMetadata_.set(controls::ColourGains, + { gains[0], gains[1] }); + break; + } + + case controls::BRIGHTNESS: { + RPiController::ContrastAlgorithm *contrast = dynamic_cast( + controller_.getAlgorithm("contrast")); + if (!contrast) { + LOG(IPARPI, Warning) + << "Could not set BRIGHTNESS - no contrast algorithm"; + break; + } + + contrast->setBrightness(ctrl.second.get() * 65536); + libcameraMetadata_.set(controls::Brightness, + ctrl.second.get()); + break; + } + + case controls::CONTRAST: { + RPiController::ContrastAlgorithm *contrast = dynamic_cast( + controller_.getAlgorithm("contrast")); + if (!contrast) { + LOG(IPARPI, Warning) + << "Could not set CONTRAST - no contrast algorithm"; + break; + } + + contrast->setContrast(ctrl.second.get()); + libcameraMetadata_.set(controls::Contrast, + ctrl.second.get()); + break; + } + + case controls::SATURATION: { + RPiController::CcmAlgorithm *ccm = dynamic_cast( + controller_.getAlgorithm("ccm")); + if (!ccm) { + LOG(IPARPI, Warning) + << "Could not set SATURATION - no ccm algorithm"; + break; + } + + ccm->setSaturation(ctrl.second.get()); + libcameraMetadata_.set(controls::Saturation, + ctrl.second.get()); + break; + } + + case controls::SHARPNESS: { + RPiController::SharpenAlgorithm *sharpen = dynamic_cast( + controller_.getAlgorithm("sharpen")); + if (!sharpen) { + LOG(IPARPI, Warning) + << "Could not set SHARPNESS - no sharpen algorithm"; + break; + } + + sharpen->setStrength(ctrl.second.get()); + libcameraMetadata_.set(controls::Sharpness, + ctrl.second.get()); + break; + } + + case controls::SCALER_CROP: { + /* We do nothing with this, but should avoid the warning below. */ + break; + } + + case controls::FRAME_DURATION_LIMITS: { + auto frameDurations = ctrl.second.get>(); + applyFrameDurations(frameDurations[0] * 1.0us, frameDurations[1] * 1.0us); + break; + } + + case controls::NOISE_REDUCTION_MODE: { + RPiController::DenoiseAlgorithm *sdn = dynamic_cast( + controller_.getAlgorithm("SDN")); + if (!sdn) { + LOG(IPARPI, Warning) + << "Could not set NOISE_REDUCTION_MODE - no SDN algorithm"; + break; + } + + int32_t idx = ctrl.second.get(); + auto mode = DenoiseModeTable.find(idx); + if (mode != DenoiseModeTable.end()) { + sdn->setMode(mode->second); + + /* + * \todo If the colour denoise is not going to run due to an + * analysis image resolution or format mismatch, we should + * report the status correctly in the metadata. + */ + libcameraMetadata_.set(controls::draft::NoiseReductionMode, idx); + } else { + LOG(IPARPI, Error) << "Noise reduction mode " << idx + << " not recognised"; + } + break; + } + + case controls::AF_MODE: + break; /* We already handled this one above */ + + case controls::AF_RANGE: { + AfAlgorithm *af = dynamic_cast(controller_.getAlgorithm("af")); + if (!af) { + LOG(IPARPI, Warning) + << "Could not set AF_RANGE - no focus algorithm"; + break; + } + + auto range = AfRangeTable.find(ctrl.second.get()); + if (range == AfRangeTable.end()) { + LOG(IPARPI, Error) << "AF range " << ctrl.second.get() + << " not recognised"; + break; + } + af->setRange(range->second); + break; + } + + case controls::AF_SPEED: { + AfAlgorithm *af = dynamic_cast(controller_.getAlgorithm("af")); + if (!af) { + LOG(IPARPI, Warning) + << "Could not set AF_SPEED - no focus algorithm"; + break; + } + + AfAlgorithm::AfSpeed speed = ctrl.second.get() == controls::AfSpeedFast ? + AfAlgorithm::AfSpeedFast : AfAlgorithm::AfSpeedNormal; + af->setSpeed(speed); + break; + } + + case controls::AF_METERING: { + AfAlgorithm *af = dynamic_cast(controller_.getAlgorithm("af")); + if (!af) { + LOG(IPARPI, Warning) + << "Could not set AF_METERING - no AF algorithm"; + break; + } + af->setMetering(ctrl.second.get() == controls::AfMeteringWindows); + break; + } + + case controls::AF_WINDOWS: { + AfAlgorithm *af = dynamic_cast(controller_.getAlgorithm("af")); + if (!af) { + LOG(IPARPI, Warning) + << "Could not set AF_WINDOWS - no AF algorithm"; + break; + } + af->setWindows(ctrl.second.get>()); + break; + } + + case controls::AF_PAUSE: { + AfAlgorithm *af = dynamic_cast(controller_.getAlgorithm("af")); + if (!af || af->getMode() != AfAlgorithm::AfModeContinuous) { + LOG(IPARPI, Warning) + << "Could not set AF_PAUSE - no AF algorithm or not Continuous"; + break; + } + auto pause = AfPauseTable.find(ctrl.second.get()); + if (pause == AfPauseTable.end()) { + LOG(IPARPI, Error) << "AF pause " << ctrl.second.get() + << " not recognised"; + break; + } + af->pause(pause->second); + break; + } + + case controls::AF_TRIGGER: { + AfAlgorithm *af = dynamic_cast(controller_.getAlgorithm("af")); + if (!af || af->getMode() != AfAlgorithm::AfModeAuto) { + LOG(IPARPI, Warning) + << "Could not set AF_TRIGGER - no AF algorithm or not Auto"; + break; + } else { + if (ctrl.second.get() == controls::AfTriggerStart) + af->triggerScan(); + else + af->cancelScan(); + } + break; + } + + case controls::LENS_POSITION: { + AfAlgorithm *af = dynamic_cast(controller_.getAlgorithm("af")); + if (af) { + int32_t hwpos; + if (af->setLensPosition(ctrl.second.get(), &hwpos)) { + ControlList lensCtrls(lensCtrls_); + lensCtrls.set(V4L2_CID_FOCUS_ABSOLUTE, hwpos); + setLensControls.emit(lensCtrls); + } + } else { + LOG(IPARPI, Warning) + << "Could not set LENS_POSITION - no AF algorithm"; + } + break; + } + + default: + LOG(IPARPI, Warning) + << "Ctrl " << controls::controls.at(ctrl.first)->name() + << " is not handled."; + break; + } + } +} + +void IPARPi::returnEmbeddedBuffer(unsigned int bufferId) +{ + embeddedComplete.emit(bufferId); +} + +void IPARPi::prepareISP(const ISPConfig &data) +{ + int64_t frameTimestamp = data.controls.get(controls::SensorTimestamp).value_or(0); + unsigned int ipaContext = data.ipaContext % rpiMetadata_.size(); + RPiController::Metadata &rpiMetadata = rpiMetadata_[ipaContext]; + Span embeddedBuffer; + + rpiMetadata.clear(); + fillDeviceStatus(data.controls, ipaContext); + + if (data.embeddedBufferPresent) { + /* + * Pipeline handler has supplied us with an embedded data buffer, + * we must pass it to the CamHelper for parsing. + */ + auto it = buffers_.find(data.embeddedBufferId); + ASSERT(it != buffers_.end()); + embeddedBuffer = it->second.planes()[0]; + } + + /* + * AGC wants to know the algorithm status from the time it actioned the + * sensor exposure/gain changes. So fetch it from the metadata list + * indexed by the IPA cookie returned, and put it in the current frame + * metadata. + */ + AgcStatus agcStatus; + RPiController::Metadata &delayedMetadata = rpiMetadata_[data.delayContext]; + if (!delayedMetadata.get("agc.status", agcStatus)) + rpiMetadata.set("agc.delayed_status", agcStatus); + + /* + * This may overwrite the DeviceStatus using values from the sensor + * metadata, and may also do additional custom processing. + */ + helper_->prepare(embeddedBuffer, rpiMetadata); + + /* Done with embedded data now, return to pipeline handler asap. */ + if (data.embeddedBufferPresent) + returnEmbeddedBuffer(data.embeddedBufferId); + + /* Allow a 10% margin on the comparison below. */ + Duration delta = (frameTimestamp - lastRunTimestamp_) * 1.0ns; + if (lastRunTimestamp_ && frameCount_ > dropFrameCount_ && + delta < controllerMinFrameDuration * 0.9) { + /* + * Ensure we merge the previous frame's metadata with the current + * frame. This will not overwrite exposure/gain values for the + * current frame, or any other bits of metadata that were added + * in helper_->Prepare(). + */ + RPiController::Metadata &lastMetadata = + rpiMetadata_[(ipaContext ? ipaContext : rpiMetadata_.size()) - 1]; + rpiMetadata.mergeCopy(lastMetadata); + processPending_ = false; + return; + } + + lastRunTimestamp_ = frameTimestamp; + processPending_ = true; + + ControlList ctrls(ispCtrls_); + + controller_.prepare(&rpiMetadata); + + /* Lock the metadata buffer to avoid constant locks/unlocks. */ + std::unique_lock lock(rpiMetadata); + + AwbStatus *awbStatus = rpiMetadata.getLocked("awb.status"); + if (awbStatus) + applyAWB(awbStatus, ctrls); + + CcmStatus *ccmStatus = rpiMetadata.getLocked("ccm.status"); + if (ccmStatus) + applyCCM(ccmStatus, ctrls); + + AgcStatus *dgStatus = rpiMetadata.getLocked("agc.status"); + if (dgStatus) + applyDG(dgStatus, ctrls); + + AlscStatus *lsStatus = rpiMetadata.getLocked("alsc.status"); + if (lsStatus) + applyLS(lsStatus, ctrls); + + ContrastStatus *contrastStatus = rpiMetadata.getLocked("contrast.status"); + if (contrastStatus) + applyGamma(contrastStatus, ctrls); + + BlackLevelStatus *blackLevelStatus = rpiMetadata.getLocked("black_level.status"); + if (blackLevelStatus) + applyBlackLevel(blackLevelStatus, ctrls); + + GeqStatus *geqStatus = rpiMetadata.getLocked("geq.status"); + if (geqStatus) + applyGEQ(geqStatus, ctrls); + + DenoiseStatus *denoiseStatus = rpiMetadata.getLocked("denoise.status"); + if (denoiseStatus) + applyDenoise(denoiseStatus, ctrls); + + SharpenStatus *sharpenStatus = rpiMetadata.getLocked("sharpen.status"); + if (sharpenStatus) + applySharpen(sharpenStatus, ctrls); + + DpcStatus *dpcStatus = rpiMetadata.getLocked("dpc.status"); + if (dpcStatus) + applyDPC(dpcStatus, ctrls); + + const AfStatus *afStatus = rpiMetadata.getLocked("af.status"); + if (afStatus) { + ControlList lensctrls(lensCtrls_); + applyAF(afStatus, lensctrls); + if (!lensctrls.empty()) + setLensControls.emit(lensctrls); + } + + if (!ctrls.empty()) + setIspControls.emit(ctrls); +} + +void IPARPi::fillDeviceStatus(const ControlList &sensorControls, unsigned int ipaContext) +{ + DeviceStatus deviceStatus = {}; + + int32_t exposureLines = sensorControls.get(V4L2_CID_EXPOSURE).get(); + int32_t gainCode = sensorControls.get(V4L2_CID_ANALOGUE_GAIN).get(); + int32_t vblank = sensorControls.get(V4L2_CID_VBLANK).get(); + int32_t hblank = sensorControls.get(V4L2_CID_HBLANK).get(); + + deviceStatus.lineLength = helper_->hblankToLineLength(hblank); + deviceStatus.shutterSpeed = helper_->exposure(exposureLines, deviceStatus.lineLength); + deviceStatus.analogueGain = helper_->gain(gainCode); + deviceStatus.frameLength = mode_.height + vblank; + + RPiController::AfAlgorithm *af = dynamic_cast( + controller_.getAlgorithm("af")); + if (af) + deviceStatus.lensPosition = af->getLensPosition(); + + LOG(IPARPI, Debug) << "Metadata - " << deviceStatus; + + rpiMetadata_[ipaContext].set("device.status", deviceStatus); +} + +RPiController::StatisticsPtr IPARPi::fillStatistics(bcm2835_isp_stats *stats) const +{ + using namespace RPiController; + + const Controller::HardwareConfig &hw = controller_.getHardwareConfig(); + unsigned int i; + StatisticsPtr statistics = + std::make_unique(Statistics::AgcStatsPos::PreWb, Statistics::ColourStatsPos::PostLsc); + + /* RGB histograms are not used, so do not populate them. */ + statistics->yHist = RPiController::Histogram(stats->hist[0].g_hist, + hw.numHistogramBins); + + /* All region sums are based on a 16-bit normalised pipeline bit-depth. */ + unsigned int scale = Statistics::NormalisationFactorPow2 - hw.pipelineWidth; + + statistics->awbRegions.init(hw.awbRegions); + for (i = 0; i < statistics->awbRegions.numRegions(); i++) + statistics->awbRegions.set(i, { { stats->awb_stats[i].r_sum << scale, + stats->awb_stats[i].g_sum << scale, + stats->awb_stats[i].b_sum << scale }, + stats->awb_stats[i].counted, + stats->awb_stats[i].notcounted }); + + statistics->agcRegions.init(hw.agcRegions); + for (i = 0; i < statistics->agcRegions.numRegions(); i++) + statistics->agcRegions.set(i, { { stats->agc_stats[i].r_sum << scale, + stats->agc_stats[i].g_sum << scale, + stats->agc_stats[i].b_sum << scale }, + stats->agc_stats[i].counted, + stats->awb_stats[i].notcounted }); + + statistics->focusRegions.init(hw.focusRegions); + for (i = 0; i < statistics->focusRegions.numRegions(); i++) + statistics->focusRegions.set(i, { stats->focus_stats[i].contrast_val[1][1] / 1000, + stats->focus_stats[i].contrast_val_num[1][1], + stats->focus_stats[i].contrast_val_num[1][0] }); + return statistics; +} + +void IPARPi::processStats(unsigned int bufferId, unsigned int ipaContext) +{ + RPiController::Metadata &rpiMetadata = rpiMetadata_[ipaContext]; + + auto it = buffers_.find(bufferId); + if (it == buffers_.end()) { + LOG(IPARPI, Error) << "Could not find stats buffer!"; + return; + } + + Span mem = it->second.planes()[0]; + bcm2835_isp_stats *stats = reinterpret_cast(mem.data()); + RPiController::StatisticsPtr statistics = fillStatistics(stats); + + /* Save the focus stats in the metadata structure to report out later. */ + rpiMetadata_[ipaContext].set("focus.status", statistics->focusRegions); + + helper_->process(statistics, rpiMetadata); + controller_.process(statistics, &rpiMetadata); + + struct AgcStatus agcStatus; + if (rpiMetadata.get("agc.status", agcStatus) == 0) { + ControlList ctrls(sensorCtrls_); + applyAGC(&agcStatus, ctrls); + + setDelayedControls.emit(ctrls, ipaContext); + setCameraTimeoutValue(); + } +} + +void IPARPi::setCameraTimeoutValue() +{ + /* + * Take the maximum value of the exposure queue as the camera timeout + * value to pass back to the pipeline handler. Only signal if it has changed + * from the last set value. + */ + auto max = std::max_element(frameLengths_.begin(), frameLengths_.end()); + + if (*max != lastTimeout_) { + setCameraTimeout.emit(max->get()); + lastTimeout_ = *max; + } +} + +void IPARPi::applyAWB(const struct AwbStatus *awbStatus, ControlList &ctrls) +{ + LOG(IPARPI, Debug) << "Applying WB R: " << awbStatus->gainR << " B: " + << awbStatus->gainB; + + ctrls.set(V4L2_CID_RED_BALANCE, + static_cast(awbStatus->gainR * 1000)); + ctrls.set(V4L2_CID_BLUE_BALANCE, + static_cast(awbStatus->gainB * 1000)); +} + +void IPARPi::applyFrameDurations(Duration minFrameDuration, Duration maxFrameDuration) +{ + /* + * This will only be applied once AGC recalculations occur. + * The values may be clamped based on the sensor mode capabilities as well. + */ + minFrameDuration_ = minFrameDuration ? minFrameDuration : defaultMinFrameDuration; + maxFrameDuration_ = maxFrameDuration ? maxFrameDuration : defaultMaxFrameDuration; + minFrameDuration_ = std::clamp(minFrameDuration_, + mode_.minFrameDuration, mode_.maxFrameDuration); + maxFrameDuration_ = std::clamp(maxFrameDuration_, + mode_.minFrameDuration, mode_.maxFrameDuration); + maxFrameDuration_ = std::max(maxFrameDuration_, minFrameDuration_); + + /* Return the validated limits via metadata. */ + libcameraMetadata_.set(controls::FrameDurationLimits, + { static_cast(minFrameDuration_.get()), + static_cast(maxFrameDuration_.get()) }); + + /* + * Calculate the maximum exposure time possible for the AGC to use. + * getBlanking() will update maxShutter with the largest exposure + * value possible. + */ + Duration maxShutter = Duration::max(); + helper_->getBlanking(maxShutter, minFrameDuration_, maxFrameDuration_); + + RPiController::AgcAlgorithm *agc = dynamic_cast( + controller_.getAlgorithm("agc")); + agc->setMaxShutter(maxShutter); +} + +void IPARPi::applyAGC(const struct AgcStatus *agcStatus, ControlList &ctrls) +{ + const int32_t minGainCode = helper_->gainCode(mode_.minAnalogueGain); + const int32_t maxGainCode = helper_->gainCode(mode_.maxAnalogueGain); + int32_t gainCode = helper_->gainCode(agcStatus->analogueGain); + + /* + * Ensure anything larger than the max gain code will not be passed to + * DelayedControls. The AGC will correctly handle a lower gain returned + * by the sensor, provided it knows the actual gain used. + */ + gainCode = std::clamp(gainCode, minGainCode, maxGainCode); + + /* getBlanking might clip exposure time to the fps limits. */ + Duration exposure = agcStatus->shutterTime; + auto [vblank, hblank] = helper_->getBlanking(exposure, minFrameDuration_, maxFrameDuration_); + int32_t exposureLines = helper_->exposureLines(exposure, + helper_->hblankToLineLength(hblank)); + + LOG(IPARPI, Debug) << "Applying AGC Exposure: " << exposure + << " (Shutter lines: " << exposureLines << ", AGC requested " + << agcStatus->shutterTime << ") Gain: " + << agcStatus->analogueGain << " (Gain Code: " + << gainCode << ")"; + + ctrls.set(V4L2_CID_VBLANK, static_cast(vblank)); + ctrls.set(V4L2_CID_EXPOSURE, exposureLines); + ctrls.set(V4L2_CID_ANALOGUE_GAIN, gainCode); + + /* + * At present, there is no way of knowing if a control is read-only. + * As a workaround, assume that if the minimum and maximum values of + * the V4L2_CID_HBLANK control are the same, it implies the control + * is read-only. This seems to be the case for all the cameras our IPA + * works with. + * + * \todo The control API ought to have a flag to specify if a control + * is read-only which could be used below. + */ + if (mode_.minLineLength != mode_.maxLineLength) + ctrls.set(V4L2_CID_HBLANK, static_cast(hblank)); + + /* + * Store the frame length times in a circular queue, up-to FrameLengthsQueueSize + * elements. This will be used to advertise a camera timeout value to the + * pipeline handler. + */ + frameLengths_.pop_front(); + frameLengths_.push_back(helper_->exposure(vblank + mode_.height, + helper_->hblankToLineLength(hblank))); +} + +void IPARPi::applyDG(const struct AgcStatus *dgStatus, ControlList &ctrls) +{ + ctrls.set(V4L2_CID_DIGITAL_GAIN, + static_cast(dgStatus->digitalGain * 1000)); +} + +void IPARPi::applyCCM(const struct CcmStatus *ccmStatus, ControlList &ctrls) +{ + bcm2835_isp_custom_ccm ccm; + + for (int i = 0; i < 9; i++) { + ccm.ccm.ccm[i / 3][i % 3].den = 1000; + ccm.ccm.ccm[i / 3][i % 3].num = 1000 * ccmStatus->matrix[i]; + } + + ccm.enabled = 1; + ccm.ccm.offsets[0] = ccm.ccm.offsets[1] = ccm.ccm.offsets[2] = 0; + + ControlValue c(Span{ reinterpret_cast(&ccm), + sizeof(ccm) }); + ctrls.set(V4L2_CID_USER_BCM2835_ISP_CC_MATRIX, c); +} + +void IPARPi::applyGamma(const struct ContrastStatus *contrastStatus, ControlList &ctrls) +{ + const unsigned int numGammaPoints = controller_.getHardwareConfig().numGammaPoints; + struct bcm2835_isp_gamma gamma; + + for (unsigned int i = 0; i < numGammaPoints - 1; i++) { + int x = i < 16 ? i * 1024 + : (i < 24 ? (i - 16) * 2048 + 16384 + : (i - 24) * 4096 + 32768); + gamma.x[i] = x; + gamma.y[i] = std::min(65535, contrastStatus->gammaCurve.eval(x)); + } + + gamma.x[numGammaPoints - 1] = 65535; + gamma.y[numGammaPoints - 1] = 65535; + gamma.enabled = 1; + + ControlValue c(Span{ reinterpret_cast(&gamma), + sizeof(gamma) }); + ctrls.set(V4L2_CID_USER_BCM2835_ISP_GAMMA, c); +} + +void IPARPi::applyBlackLevel(const struct BlackLevelStatus *blackLevelStatus, ControlList &ctrls) +{ + bcm2835_isp_black_level blackLevel; + + blackLevel.enabled = 1; + blackLevel.black_level_r = blackLevelStatus->blackLevelR; + blackLevel.black_level_g = blackLevelStatus->blackLevelG; + blackLevel.black_level_b = blackLevelStatus->blackLevelB; + + ControlValue c(Span{ reinterpret_cast(&blackLevel), + sizeof(blackLevel) }); + ctrls.set(V4L2_CID_USER_BCM2835_ISP_BLACK_LEVEL, c); +} + +void IPARPi::applyGEQ(const struct GeqStatus *geqStatus, ControlList &ctrls) +{ + bcm2835_isp_geq geq; + + geq.enabled = 1; + geq.offset = geqStatus->offset; + geq.slope.den = 1000; + geq.slope.num = 1000 * geqStatus->slope; + + ControlValue c(Span{ reinterpret_cast(&geq), + sizeof(geq) }); + ctrls.set(V4L2_CID_USER_BCM2835_ISP_GEQ, c); +} + +void IPARPi::applyDenoise(const struct DenoiseStatus *denoiseStatus, ControlList &ctrls) +{ + using RPiController::DenoiseMode; + + bcm2835_isp_denoise denoise; + DenoiseMode mode = static_cast(denoiseStatus->mode); + + denoise.enabled = mode != DenoiseMode::Off; + denoise.constant = denoiseStatus->noiseConstant; + denoise.slope.num = 1000 * denoiseStatus->noiseSlope; + denoise.slope.den = 1000; + denoise.strength.num = 1000 * denoiseStatus->strength; + denoise.strength.den = 1000; + + /* Set the CDN mode to match the SDN operating mode. */ + bcm2835_isp_cdn cdn; + switch (mode) { + case DenoiseMode::ColourFast: + cdn.enabled = 1; + cdn.mode = CDN_MODE_FAST; + break; + case DenoiseMode::ColourHighQuality: + cdn.enabled = 1; + cdn.mode = CDN_MODE_HIGH_QUALITY; + break; + default: + cdn.enabled = 0; + } + + ControlValue c(Span{ reinterpret_cast(&denoise), + sizeof(denoise) }); + ctrls.set(V4L2_CID_USER_BCM2835_ISP_DENOISE, c); + + c = ControlValue(Span{ reinterpret_cast(&cdn), + sizeof(cdn) }); + ctrls.set(V4L2_CID_USER_BCM2835_ISP_CDN, c); +} + +void IPARPi::applySharpen(const struct SharpenStatus *sharpenStatus, ControlList &ctrls) +{ + bcm2835_isp_sharpen sharpen; + + sharpen.enabled = 1; + sharpen.threshold.num = 1000 * sharpenStatus->threshold; + sharpen.threshold.den = 1000; + sharpen.strength.num = 1000 * sharpenStatus->strength; + sharpen.strength.den = 1000; + sharpen.limit.num = 1000 * sharpenStatus->limit; + sharpen.limit.den = 1000; + + ControlValue c(Span{ reinterpret_cast(&sharpen), + sizeof(sharpen) }); + ctrls.set(V4L2_CID_USER_BCM2835_ISP_SHARPEN, c); +} + +void IPARPi::applyDPC(const struct DpcStatus *dpcStatus, ControlList &ctrls) +{ + bcm2835_isp_dpc dpc; + + dpc.enabled = 1; + dpc.strength = dpcStatus->strength; + + ControlValue c(Span{ reinterpret_cast(&dpc), + sizeof(dpc) }); + ctrls.set(V4L2_CID_USER_BCM2835_ISP_DPC, c); +} + +void IPARPi::applyLS(const struct AlscStatus *lsStatus, ControlList &ctrls) +{ + /* + * Program lens shading tables into pipeline. + * Choose smallest cell size that won't exceed 63x48 cells. + */ + const int cellSizes[] = { 16, 32, 64, 128, 256 }; + unsigned int numCells = std::size(cellSizes); + unsigned int i, w, h, cellSize; + for (i = 0; i < numCells; i++) { + cellSize = cellSizes[i]; + w = (mode_.width + cellSize - 1) / cellSize; + h = (mode_.height + cellSize - 1) / cellSize; + if (w < 64 && h <= 48) + break; + } + + if (i == numCells) { + LOG(IPARPI, Error) << "Cannot find cell size"; + return; + } + + /* We're going to supply corner sampled tables, 16 bit samples. */ + w++, h++; + bcm2835_isp_lens_shading ls = { + .enabled = 1, + .grid_cell_size = cellSize, + .grid_width = w, + .grid_stride = w, + .grid_height = h, + /* .dmabuf will be filled in by pipeline handler. */ + .dmabuf = 0, + .ref_transform = 0, + .corner_sampled = 1, + .gain_format = GAIN_FORMAT_U4P10 + }; + + if (!lsTable_ || w * h * 4 * sizeof(uint16_t) > MaxLsGridSize) { + LOG(IPARPI, Error) << "Do not have a correctly allocate lens shading table!"; + return; + } + + if (lsStatus) { + /* Format will be u4.10 */ + uint16_t *grid = static_cast(lsTable_); + + resampleTable(grid, lsStatus->r, w, h); + resampleTable(grid + w * h, lsStatus->g, w, h); + std::memcpy(grid + 2 * w * h, grid + w * h, w * h * sizeof(uint16_t)); + resampleTable(grid + 3 * w * h, lsStatus->b, w, h); + } + + ControlValue c(Span{ reinterpret_cast(&ls), + sizeof(ls) }); + ctrls.set(V4L2_CID_USER_BCM2835_ISP_LENS_SHADING, c); +} + +void IPARPi::applyAF(const struct AfStatus *afStatus, ControlList &lensCtrls) +{ + if (afStatus->lensSetting) { + ControlValue v(afStatus->lensSetting.value()); + lensCtrls.set(V4L2_CID_FOCUS_ABSOLUTE, v); + } +} + +/* + * Resamples a 16x12 table with central sampling to destW x destH with corner + * sampling. + */ +void IPARPi::resampleTable(uint16_t dest[], const std::vector &src, + int destW, int destH) +{ + /* + * Precalculate and cache the x sampling locations and phases to + * save recomputing them on every row. + */ + assert(destW > 1 && destH > 1 && destW <= 64); + int xLo[64], xHi[64]; + double xf[64]; + double x = -0.5, xInc = 16.0 / (destW - 1); + for (int i = 0; i < destW; i++, x += xInc) { + xLo[i] = floor(x); + xf[i] = x - xLo[i]; + xHi[i] = xLo[i] < 15 ? xLo[i] + 1 : 15; + xLo[i] = xLo[i] > 0 ? xLo[i] : 0; + } + + /* Now march over the output table generating the new values. */ + double y = -0.5, yInc = 12.0 / (destH - 1); + for (int j = 0; j < destH; j++, y += yInc) { + int yLo = floor(y); + double yf = y - yLo; + int yHi = yLo < 11 ? yLo + 1 : 11; + yLo = yLo > 0 ? yLo : 0; + double const *rowAbove = src.data() + yLo * 16; + double const *rowBelow = src.data() + yHi * 16; + for (int i = 0; i < destW; i++) { + double above = rowAbove[xLo[i]] * (1 - xf[i]) + rowAbove[xHi[i]] * xf[i]; + double below = rowBelow[xLo[i]] * (1 - xf[i]) + rowBelow[xHi[i]] * xf[i]; + int result = floor(1024 * (above * (1 - yf) + below * yf) + .5); + *(dest++) = result > 16383 ? 16383 : result; /* want u4.10 */ + } + } +} + +} /* namespace ipa::RPi */ + +/* + * External IPA module interface + */ +extern "C" { +const struct IPAModuleInfo ipaModuleInfo = { + IPA_MODULE_API_VERSION, + 1, + "PipelineHandlerRPi", + "rpi/vc4", +}; + +IPAInterface *ipaCreate() +{ + return new ipa::RPi::IPARPi(); +} + +} /* extern "C" */ + +} /* namespace libcamera */ diff --git a/src/libcamera/pipeline/raspberrypi/data/example.yaml b/src/libcamera/pipeline/raspberrypi/data/example.yaml deleted file mode 100644 index c90f518f..00000000 --- a/src/libcamera/pipeline/raspberrypi/data/example.yaml +++ /dev/null @@ -1,46 +0,0 @@ -{ - "version": 1.0, - "target": "bcm2835", - - "pipeline_handler": - { - # The minimum number of internal buffers to be allocated for - # Unicam. This value must be greater than 0, but less than or - # equal to min_total_unicam_buffers. - # - # A larger number of internal buffers can reduce the occurrence - # of frame drops during high CPU loads, but might also cause - # additional latency in the system. - # - # Note that the pipeline handler might override this value and - # not allocate any internal buffers if it knows they will never - # be used. For example if the RAW stream is marked as mandatory - # and there are no dropped frames signalled for algorithm - # convergence. - # - # "min_unicam_buffers": 2, - - # The minimum total (internal + external) buffer count used for - # Unicam. The number of internal buffers allocated for Unicam is - # given by: - # - # internal buffer count = max(min_unicam_buffers, - # min_total_unicam_buffers - external buffer count) - # - # "min_total_unicam_buffers": 4, - - # Override any request from the IPA to drop a number of startup - # frames. - # - # "disable_startup_frame_drops": false, - - # Custom timeout value (in ms) for Unicam to use. This overrides - # the value computed by the pipeline handler based on frame - # durations. - # - # Set this value to 0 to use the pipeline handler computed - # timeout value. - # - # "unicam_timeout_value_ms": 0, - } -} diff --git a/src/libcamera/pipeline/raspberrypi/data/meson.build b/src/libcamera/pipeline/raspberrypi/data/meson.build deleted file mode 100644 index 1c70433b..00000000 --- a/src/libcamera/pipeline/raspberrypi/data/meson.build +++ /dev/null @@ -1,8 +0,0 @@ -# SPDX-License-Identifier: CC0-1.0 - -conf_files = files([ - 'example.yaml', -]) - -install_data(conf_files, - install_dir : pipeline_data_dir / 'raspberrypi') diff --git a/src/libcamera/pipeline/raspberrypi/delayed_controls.cpp b/src/libcamera/pipeline/raspberrypi/delayed_controls.cpp deleted file mode 100644 index 3db92e7d..00000000 --- a/src/libcamera/pipeline/raspberrypi/delayed_controls.cpp +++ /dev/null @@ -1,293 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -/* - * Copyright (C) 2020, Raspberry Pi Ltd - * - * delayed_controls.cpp - Helper to deal with controls that take effect with a delay - * - * Note: This has been forked from the libcamera core implementation. - */ - -#include "delayed_controls.h" - -#include - -#include - -#include "libcamera/internal/v4l2_device.h" - -/** - * \file delayed_controls.h - * \brief Helper to deal with controls that take effect with a delay - */ - -namespace libcamera { - -LOG_DEFINE_CATEGORY(RPiDelayedControls) - -namespace RPi { - -/** - * \class DelayedControls - * \brief Helper to deal with controls that take effect with a delay - * - * Some sensor controls take effect with a delay as the sensor needs time to - * adjust, for example exposure and analog gain. This is a helper class to deal - * with such controls and the intended users are pipeline handlers. - * - * The idea is to extend the concept of the buffer depth of a pipeline the - * application needs to maintain to also cover controls. Just as with buffer - * depth if the application keeps the number of requests queued above the - * control depth the controls are guaranteed to take effect for the correct - * request. The control depth is determined by the control with the greatest - * delay. - */ - -/** - * \struct DelayedControls::ControlParams - * \brief Parameters associated with controls handled by the \a DelayedControls - * helper class - * - * \var ControlParams::delay - * \brief Frame delay from setting the control on a sensor device to when it is - * consumed during framing. - * - * \var ControlParams::priorityWrite - * \brief Flag to indicate that this control must be applied ahead of, and - * separately from the other controls. - * - * Typically set for the \a V4L2_CID_VBLANK control so that the device driver - * does not reject \a V4L2_CID_EXPOSURE control values that may be outside of - * the existing vertical blanking specified bounds, but are within the new - * blanking bounds. - */ - -/** - * \brief Construct a DelayedControls instance - * \param[in] device The V4L2 device the controls have to be applied to - * \param[in] controlParams Map of the numerical V4L2 control ids to their - * associated control parameters. - * - * The control parameters comprise of delays (in frames) and a priority write - * flag. If this flag is set, the relevant control is written separately from, - * and ahead of the rest of the batched controls. - * - * Only controls specified in \a controlParams are handled. If it's desired to - * mix delayed controls and controls that take effect immediately the immediate - * controls must be listed in the \a controlParams map with a delay value of 0. - */ -DelayedControls::DelayedControls(V4L2Device *device, - const std::unordered_map &controlParams) - : device_(device), maxDelay_(0) -{ - const ControlInfoMap &controls = device_->controls(); - - /* - * Create a map of control ids to delays for controls exposed by the - * device. - */ - for (auto const ¶m : controlParams) { - auto it = controls.find(param.first); - if (it == controls.end()) { - LOG(RPiDelayedControls, Error) - << "Delay request for control id " - << utils::hex(param.first) - << " but control is not exposed by device " - << device_->deviceNode(); - continue; - } - - const ControlId *id = it->first; - - controlParams_[id] = param.second; - - LOG(RPiDelayedControls, Debug) - << "Set a delay of " << controlParams_[id].delay - << " and priority write flag " << controlParams_[id].priorityWrite - << " for " << id->name(); - - maxDelay_ = std::max(maxDelay_, controlParams_[id].delay); - } - - reset(0); -} - -/** - * \brief Reset state machine - * - * Resets the state machine to a starting position based on control values - * retrieved from the device. - */ -void DelayedControls::reset(unsigned int cookie) -{ - queueCount_ = 1; - writeCount_ = 0; - cookies_[0] = cookie; - - /* Retrieve control as reported by the device. */ - std::vector ids; - for (auto const ¶m : controlParams_) - ids.push_back(param.first->id()); - - ControlList controls = device_->getControls(ids); - - /* Seed the control queue with the controls reported by the device. */ - values_.clear(); - for (const auto &ctrl : controls) { - const ControlId *id = device_->controls().idmap().at(ctrl.first); - /* - * Do not mark this control value as updated, it does not need - * to be written to to device on startup. - */ - values_[id][0] = Info(ctrl.second, false); - } -} - -/** - * \brief Push a set of controls on the queue - * \param[in] controls List of controls to add to the device queue - * - * Push a set of controls to the control queue. This increases the control queue - * depth by one. - * - * \returns true if \a controls are accepted, or false otherwise - */ -bool DelayedControls::push(const ControlList &controls, const unsigned int cookie) -{ - /* Copy state from previous frame. */ - for (auto &ctrl : values_) { - Info &info = ctrl.second[queueCount_]; - info = values_[ctrl.first][queueCount_ - 1]; - info.updated = false; - } - - /* Update with new controls. */ - const ControlIdMap &idmap = device_->controls().idmap(); - for (const auto &control : controls) { - const auto &it = idmap.find(control.first); - if (it == idmap.end()) { - LOG(RPiDelayedControls, Warning) - << "Unknown control " << control.first; - return false; - } - - const ControlId *id = it->second; - - if (controlParams_.find(id) == controlParams_.end()) - return false; - - Info &info = values_[id][queueCount_]; - - info = Info(control.second); - - LOG(RPiDelayedControls, Debug) - << "Queuing " << id->name() - << " to " << info.toString() - << " at index " << queueCount_; - } - - cookies_[queueCount_] = cookie; - queueCount_++; - - return true; -} - -/** - * \brief Read back controls in effect at a sequence number - * \param[in] sequence The sequence number to get controls for - * - * Read back what controls where in effect at a specific sequence number. The - * history is a ring buffer of 16 entries where new and old values coexist. It's - * the callers responsibility to not read too old sequence numbers that have been - * pushed out of the history. - * - * Historic values are evicted by pushing new values onto the queue using - * push(). The max history from the current sequence number that yields valid - * values are thus 16 minus number of controls pushed. - * - * \return The controls at \a sequence number - */ -std::pair DelayedControls::get(uint32_t sequence) -{ - unsigned int index = std::max(0, sequence - maxDelay_); - - ControlList out(device_->controls()); - for (const auto &ctrl : values_) { - const ControlId *id = ctrl.first; - const Info &info = ctrl.second[index]; - - out.set(id->id(), info); - - LOG(RPiDelayedControls, Debug) - << "Reading " << id->name() - << " to " << info.toString() - << " at index " << index; - } - - return { out, cookies_[index] }; -} - -/** - * \brief Inform DelayedControls of the start of a new frame - * \param[in] sequence Sequence number of the frame that started - * - * Inform the state machine that a new frame has started and of its sequence - * number. Any user of these helpers is responsible to inform the helper about - * the start of any frame. This can be connected with ease to the start of a - * exposure (SOE) V4L2 event. - */ -void DelayedControls::applyControls(uint32_t sequence) -{ - LOG(RPiDelayedControls, Debug) << "frame " << sequence << " started"; - - /* - * Create control list peeking ahead in the value queue to ensure - * values are set in time to satisfy the sensor delay. - */ - ControlList out(device_->controls()); - for (auto &ctrl : values_) { - const ControlId *id = ctrl.first; - unsigned int delayDiff = maxDelay_ - controlParams_[id].delay; - unsigned int index = std::max(0, writeCount_ - delayDiff); - Info &info = ctrl.second[index]; - - if (info.updated) { - if (controlParams_[id].priorityWrite) { - /* - * This control must be written now, it could - * affect validity of the other controls. - */ - ControlList priority(device_->controls()); - priority.set(id->id(), info); - device_->setControls(&priority); - } else { - /* - * Batch up the list of controls and write them - * at the end of the function. - */ - out.set(id->id(), info); - } - - LOG(RPiDelayedControls, Debug) - << "Setting " << id->name() - << " to " << info.toString() - << " at index " << index; - - /* Done with this update, so mark as completed. */ - info.updated = false; - } - } - - writeCount_ = sequence + 1; - - while (writeCount_ > queueCount_) { - LOG(RPiDelayedControls, Debug) - << "Queue is empty, auto queue no-op."; - push({}, cookies_[queueCount_ - 1]); - } - - device_->setControls(&out); -} - -} /* namespace RPi */ - -} /* namespace libcamera */ diff --git a/src/libcamera/pipeline/raspberrypi/delayed_controls.h b/src/libcamera/pipeline/raspberrypi/delayed_controls.h deleted file mode 100644 index 61f755f0..00000000 --- a/src/libcamera/pipeline/raspberrypi/delayed_controls.h +++ /dev/null @@ -1,87 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -/* - * Copyright (C) 2020, Raspberry Pi Ltd - * - * delayed_controls.h - Helper to deal with controls that take effect with a delay - * - * Note: This has been forked from the libcamera core implementation. - */ - -#pragma once - -#include -#include -#include - -#include - -namespace libcamera { - -class V4L2Device; - -namespace RPi { - -class DelayedControls -{ -public: - struct ControlParams { - unsigned int delay; - bool priorityWrite; - }; - - DelayedControls(V4L2Device *device, - const std::unordered_map &controlParams); - - void reset(unsigned int cookie); - - bool push(const ControlList &controls, unsigned int cookie); - std::pair get(uint32_t sequence); - - void applyControls(uint32_t sequence); - -private: - class Info : public ControlValue - { - public: - Info() - : updated(false) - { - } - - Info(const ControlValue &v, bool updated_ = true) - : ControlValue(v), updated(updated_) - { - } - - bool updated; - }; - - static constexpr int listSize = 16; - template - class RingBuffer : public std::array - { - public: - T &operator[](unsigned int index) - { - return std::array::operator[](index % listSize); - } - - const T &operator[](unsigned int index) const - { - return std::array::operator[](index % listSize); - } - }; - - V4L2Device *device_; - std::unordered_map controlParams_; - unsigned int maxDelay_; - - uint32_t queueCount_; - uint32_t writeCount_; - std::unordered_map> values_; - RingBuffer cookies_; -}; - -} /* namespace RPi */ - -} /* namespace libcamera */ diff --git a/src/libcamera/pipeline/raspberrypi/dma_heaps.cpp b/src/libcamera/pipeline/raspberrypi/dma_heaps.cpp deleted file mode 100644 index 317b1fc1..00000000 --- a/src/libcamera/pipeline/raspberrypi/dma_heaps.cpp +++ /dev/null @@ -1,90 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -/* - * Copyright (C) 2020, Raspberry Pi Ltd - * - * dma_heaps.h - Helper class for dma-heap allocations. - */ - -#include "dma_heaps.h" - -#include -#include -#include -#include -#include -#include - -#include - -/* - * /dev/dma-heap/linux,cma is the dma-heap allocator, which allows dmaheap-cma - * to only have to worry about importing. - * - * Annoyingly, should the cma heap size be specified on the kernel command line - * instead of DT, the heap gets named "reserved" instead. - */ -static constexpr std::array heapNames = { - "/dev/dma_heap/linux,cma", - "/dev/dma_heap/reserved" -}; - -namespace libcamera { - -LOG_DECLARE_CATEGORY(RPI) - -namespace RPi { - -DmaHeap::DmaHeap() -{ - for (const char *name : heapNames) { - int ret = ::open(name, O_RDWR | O_CLOEXEC, 0); - if (ret < 0) { - ret = errno; - LOG(RPI, Debug) << "Failed to open " << name << ": " - << strerror(ret); - continue; - } - - dmaHeapHandle_ = UniqueFD(ret); - break; - } - - if (!dmaHeapHandle_.isValid()) - LOG(RPI, Error) << "Could not open any dmaHeap device"; -} - -DmaHeap::~DmaHeap() = default; - -UniqueFD DmaHeap::alloc(const char *name, std::size_t size) -{ - int ret; - - if (!name) - return {}; - - struct dma_heap_allocation_data alloc = {}; - - alloc.len = size; - alloc.fd_flags = O_CLOEXEC | O_RDWR; - - ret = ::ioctl(dmaHeapHandle_.get(), DMA_HEAP_IOCTL_ALLOC, &alloc); - if (ret < 0) { - LOG(RPI, Error) << "dmaHeap allocation failure for " - << name; - return {}; - } - - UniqueFD allocFd(alloc.fd); - ret = ::ioctl(allocFd.get(), DMA_BUF_SET_NAME, name); - if (ret < 0) { - LOG(RPI, Error) << "dmaHeap naming failure for " - << name; - return {}; - } - - return allocFd; -} - -} /* namespace RPi */ - -} /* namespace libcamera */ diff --git a/src/libcamera/pipeline/raspberrypi/dma_heaps.h b/src/libcamera/pipeline/raspberrypi/dma_heaps.h deleted file mode 100644 index 0a4a8d86..00000000 --- a/src/libcamera/pipeline/raspberrypi/dma_heaps.h +++ /dev/null @@ -1,32 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -/* - * Copyright (C) 2020, Raspberry Pi Ltd - * - * dma_heaps.h - Helper class for dma-heap allocations. - */ - -#pragma once - -#include - -#include - -namespace libcamera { - -namespace RPi { - -class DmaHeap -{ -public: - DmaHeap(); - ~DmaHeap(); - bool isValid() const { return dmaHeapHandle_.isValid(); } - UniqueFD alloc(const char *name, std::size_t size); - -private: - UniqueFD dmaHeapHandle_; -}; - -} /* namespace RPi */ - -} /* namespace libcamera */ diff --git a/src/libcamera/pipeline/raspberrypi/meson.build b/src/libcamera/pipeline/raspberrypi/meson.build deleted file mode 100644 index 59e8fb18..00000000 --- a/src/libcamera/pipeline/raspberrypi/meson.build +++ /dev/null @@ -1,10 +0,0 @@ -# SPDX-License-Identifier: CC0-1.0 - -libcamera_sources += files([ - 'delayed_controls.cpp', - 'dma_heaps.cpp', - 'raspberrypi.cpp', - 'rpi_stream.cpp', -]) - -subdir('data') diff --git a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp deleted file mode 100644 index 00600441..00000000 --- a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp +++ /dev/null @@ -1,2433 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -/* - * Copyright (C) 2019-2021, Raspberry Pi Ltd - * - * raspberrypi.cpp - Pipeline handler for Raspberry Pi devices - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include "libcamera/internal/bayer_format.h" -#include "libcamera/internal/camera.h" -#include "libcamera/internal/camera_lens.h" -#include "libcamera/internal/camera_sensor.h" -#include "libcamera/internal/device_enumerator.h" -#include "libcamera/internal/framebuffer.h" -#include "libcamera/internal/ipa_manager.h" -#include "libcamera/internal/media_device.h" -#include "libcamera/internal/pipeline_handler.h" -#include "libcamera/internal/v4l2_videodevice.h" -#include "libcamera/internal/yaml_parser.h" - -#include "delayed_controls.h" -#include "dma_heaps.h" -#include "rpi_stream.h" - -using namespace std::chrono_literals; - -namespace libcamera { - -LOG_DEFINE_CATEGORY(RPI) - -namespace { - -constexpr unsigned int defaultRawBitDepth = 12; - -/* Map of mbus codes to supported sizes reported by the sensor. */ -using SensorFormats = std::map>; - -SensorFormats populateSensorFormats(std::unique_ptr &sensor) -{ - SensorFormats formats; - - for (auto const mbusCode : sensor->mbusCodes()) - formats.emplace(mbusCode, sensor->sizes(mbusCode)); - - return formats; -} - -bool isMonoSensor(std::unique_ptr &sensor) -{ - unsigned int mbusCode = sensor->mbusCodes()[0]; - const BayerFormat &bayer = BayerFormat::fromMbusCode(mbusCode); - - return bayer.order == BayerFormat::Order::MONO; -} - -PixelFormat mbusCodeToPixelFormat(unsigned int mbus_code, - BayerFormat::Packing packingReq) -{ - BayerFormat bayer = BayerFormat::fromMbusCode(mbus_code); - - ASSERT(bayer.isValid()); - - bayer.packing = packingReq; - PixelFormat pix = bayer.toPixelFormat(); - - /* - * Not all formats (e.g. 8-bit or 16-bit Bayer formats) can have packed - * variants. So if the PixelFormat returns as invalid, use the non-packed - * conversion instead. - */ - if (!pix.isValid()) { - bayer.packing = BayerFormat::Packing::None; - pix = bayer.toPixelFormat(); - } - - return pix; -} - -V4L2DeviceFormat toV4L2DeviceFormat(const V4L2VideoDevice *dev, - const V4L2SubdeviceFormat &format, - BayerFormat::Packing packingReq) -{ - const PixelFormat pix = mbusCodeToPixelFormat(format.mbus_code, packingReq); - V4L2DeviceFormat deviceFormat; - - deviceFormat.fourcc = dev->toV4L2PixelFormat(pix); - deviceFormat.size = format.size; - deviceFormat.colorSpace = format.colorSpace; - return deviceFormat; -} - -bool isRaw(const PixelFormat &pixFmt) -{ - /* This test works for both Bayer and raw mono formats. */ - return BayerFormat::fromPixelFormat(pixFmt).isValid(); -} - -double scoreFormat(double desired, double actual) -{ - double score = desired - actual; - /* Smaller desired dimensions are preferred. */ - if (score < 0.0) - score = (-score) / 8; - /* Penalise non-exact matches. */ - if (actual != desired) - score *= 2; - - return score; -} - -V4L2SubdeviceFormat findBestFormat(const SensorFormats &formatsMap, const Size &req, unsigned int bitDepth) -{ - double bestScore = std::numeric_limits::max(), score; - V4L2SubdeviceFormat bestFormat; - bestFormat.colorSpace = ColorSpace::Raw; - - constexpr float penaltyAr = 1500.0; - constexpr float penaltyBitDepth = 500.0; - - /* Calculate the closest/best mode from the user requested size. */ - for (const auto &iter : formatsMap) { - const unsigned int mbusCode = iter.first; - const PixelFormat format = mbusCodeToPixelFormat(mbusCode, - BayerFormat::Packing::None); - const PixelFormatInfo &info = PixelFormatInfo::info(format); - - for (const Size &size : iter.second) { - double reqAr = static_cast(req.width) / req.height; - double fmtAr = static_cast(size.width) / size.height; - - /* Score the dimensions for closeness. */ - score = scoreFormat(req.width, size.width); - score += scoreFormat(req.height, size.height); - score += penaltyAr * scoreFormat(reqAr, fmtAr); - - /* Add any penalties... this is not an exact science! */ - score += utils::abs_diff(info.bitsPerPixel, bitDepth) * penaltyBitDepth; - - if (score <= bestScore) { - bestScore = score; - bestFormat.mbus_code = mbusCode; - bestFormat.size = size; - } - - LOG(RPI, Debug) << "Format: " << size - << " fmt " << format - << " Score: " << score - << " (best " << bestScore << ")"; - } - } - - return bestFormat; -} - -enum class Unicam : unsigned int { Image, Embedded }; -enum class Isp : unsigned int { Input, Output0, Output1, Stats }; - -} /* namespace */ - -class RPiCameraData : public Camera::Private -{ -public: - RPiCameraData(PipelineHandler *pipe) - : Camera::Private(pipe), state_(State::Stopped), - flipsAlterBayerOrder_(false), dropFrameCount_(0), - buffersAllocated_(false), ispOutputCount_(0) - { - } - - ~RPiCameraData() - { - freeBuffers(); - } - - void freeBuffers(); - void frameStarted(uint32_t sequence); - - int loadIPA(ipa::RPi::IPAInitResult *result); - int configureIPA(const CameraConfiguration *config, ipa::RPi::IPAConfigResult *result); - int loadPipelineConfiguration(); - - void enumerateVideoDevices(MediaLink *link); - - void statsMetadataComplete(uint32_t bufferId, const ControlList &controls); - void runIsp(uint32_t bufferId); - void embeddedComplete(uint32_t bufferId); - void setIspControls(const ControlList &controls); - void setDelayedControls(const ControlList &controls, uint32_t delayContext); - void setLensControls(const ControlList &controls); - void setCameraTimeout(uint32_t maxExposureTimeMs); - void setSensorControls(ControlList &controls); - void unicamTimeout(); - - /* bufferComplete signal handlers. */ - void unicamBufferDequeue(FrameBuffer *buffer); - void ispInputDequeue(FrameBuffer *buffer); - void ispOutputDequeue(FrameBuffer *buffer); - - void clearIncompleteRequests(); - void handleStreamBuffer(FrameBuffer *buffer, RPi::Stream *stream); - void handleExternalBuffer(FrameBuffer *buffer, RPi::Stream *stream); - void handleState(); - Rectangle scaleIspCrop(const Rectangle &ispCrop) const; - void applyScalerCrop(const ControlList &controls); - - std::unique_ptr ipa_; - - std::unique_ptr sensor_; - SensorFormats sensorFormats_; - /* Array of Unicam and ISP device streams and associated buffers/streams. */ - RPi::Device unicam_; - RPi::Device isp_; - /* The vector below is just for convenience when iterating over all streams. */ - std::vector streams_; - /* Stores the ids of the buffers mapped in the IPA. */ - std::unordered_set ipaBuffers_; - /* - * Stores a cascade of Video Mux or Bridge devices between the sensor and - * Unicam together with media link across the entities. - */ - std::vector, MediaLink *>> bridgeDevices_; - - /* DMAHEAP allocation helper. */ - RPi::DmaHeap dmaHeap_; - SharedFD lsTable_; - - std::unique_ptr delayedCtrls_; - bool sensorMetadata_; - - /* - * All the functions in this class are called from a single calling - * thread. So, we do not need to have any mutex to protect access to any - * of the variables below. - */ - enum class State { Stopped, Idle, Busy, IpaComplete, Error }; - State state_; - - bool isRunning() - { - return state_ != State::Stopped && state_ != State::Error; - } - - struct BayerFrame { - FrameBuffer *buffer; - ControlList controls; - unsigned int delayContext; - }; - - std::queue bayerQueue_; - std::queue embeddedQueue_; - std::deque requestQueue_; - - /* - * Store the "native" Bayer order (that is, with no transforms - * applied). - */ - bool flipsAlterBayerOrder_; - BayerFormat::Order nativeBayerOrder_; - - /* For handling digital zoom. */ - IPACameraSensorInfo sensorInfo_; - Rectangle ispCrop_; /* crop in ISP (camera mode) pixels */ - Rectangle scalerCrop_; /* crop in sensor native pixels */ - Size ispMinCropSize_; - - unsigned int dropFrameCount_; - - /* - * If set, this stores the value that represets a gain of one for - * the V4L2_CID_NOTIFY_GAINS control. - */ - std::optional notifyGainsUnity_; - - /* Have internal buffers been allocated? */ - bool buffersAllocated_; - - struct Config { - /* - * The minimum number of internal buffers to be allocated for - * the Unicam Image stream. - */ - unsigned int minUnicamBuffers; - /* - * The minimum total (internal + external) buffer count used for - * the Unicam Image stream. - * - * Note that: - * minTotalUnicamBuffers must be >= 1, and - * minTotalUnicamBuffers >= minUnicamBuffers - */ - unsigned int minTotalUnicamBuffers; - /* - * Override any request from the IPA to drop a number of startup - * frames. - */ - bool disableStartupFrameDrops; - /* - * Override the Unicam timeout value calculated by the IPA based - * on frame durations. - */ - unsigned int unicamTimeoutValue; - }; - - Config config_; - -private: - void checkRequestCompleted(); - void fillRequestMetadata(const ControlList &bufferControls, - Request *request); - void tryRunPipeline(); - bool findMatchingBuffers(BayerFrame &bayerFrame, FrameBuffer *&embeddedBuffer); - - unsigned int ispOutputCount_; -}; - -class RPiCameraConfiguration : public CameraConfiguration -{ -public: - RPiCameraConfiguration(const RPiCameraData *data); - - CameraConfiguration::Status validateColorSpaces(ColorSpaceFlags flags); - Status validate() override; - - /* Cache the combinedTransform_ that will be applied to the sensor */ - Transform combinedTransform_; - -private: - const RPiCameraData *data_; - - /* - * Store the colour spaces that all our streams will have. RGB format streams - * will have the same colorspace as YUV streams, with YCbCr field cleared and - * range set to full. - */ - std::optional yuvColorSpace_; - std::optional rgbColorSpace_; -}; - -class PipelineHandlerRPi : public PipelineHandler -{ -public: - PipelineHandlerRPi(CameraManager *manager); - - std::unique_ptr generateConfiguration(Camera *camera, - const StreamRoles &roles) override; - int configure(Camera *camera, CameraConfiguration *config) override; - - int exportFrameBuffers(Camera *camera, Stream *stream, - std::vector> *buffers) override; - - int start(Camera *camera, const ControlList *controls) override; - void stopDevice(Camera *camera) override; - - int queueRequestDevice(Camera *camera, Request *request) override; - - bool match(DeviceEnumerator *enumerator) override; - - void releaseDevice(Camera *camera) override; - -private: - RPiCameraData *cameraData(Camera *camera) - { - return static_cast(camera->_d()); - } - - int registerCamera(MediaDevice *unicam, MediaDevice *isp, MediaEntity *sensorEntity); - int queueAllBuffers(Camera *camera); - int prepareBuffers(Camera *camera); - void mapBuffers(Camera *camera, const RPi::BufferMap &buffers, unsigned int mask); -}; - -RPiCameraConfiguration::RPiCameraConfiguration(const RPiCameraData *data) - : CameraConfiguration(), data_(data) -{ -} - -static const std::vector validColorSpaces = { - ColorSpace::Sycc, - ColorSpace::Smpte170m, - ColorSpace::Rec709 -}; - -static std::optional findValidColorSpace(const ColorSpace &colourSpace) -{ - for (auto cs : validColorSpaces) { - if (colourSpace.primaries == cs.primaries && - colourSpace.transferFunction == cs.transferFunction) - return cs; - } - - return std::nullopt; -} - -static bool isRgb(const PixelFormat &pixFmt) -{ - const PixelFormatInfo &info = PixelFormatInfo::info(pixFmt); - return info.colourEncoding == PixelFormatInfo::ColourEncodingRGB; -} - -static bool isYuv(const PixelFormat &pixFmt) -{ - /* The code below would return true for raw mono streams, so weed those out first. */ - if (isRaw(pixFmt)) - return false; - - const PixelFormatInfo &info = PixelFormatInfo::info(pixFmt); - return info.colourEncoding == PixelFormatInfo::ColourEncodingYUV; -} - -/* - * Raspberry Pi drivers expect the following colour spaces: - * - V4L2_COLORSPACE_RAW for raw streams. - * - One of V4L2_COLORSPACE_JPEG, V4L2_COLORSPACE_SMPTE170M, V4L2_COLORSPACE_REC709 for - * non-raw streams. Other fields such as transfer function, YCbCr encoding and - * quantisation are not used. - * - * The libcamera colour spaces that we wish to use corresponding to these are therefore: - * - ColorSpace::Raw for V4L2_COLORSPACE_RAW - * - ColorSpace::Sycc for V4L2_COLORSPACE_JPEG - * - ColorSpace::Smpte170m for V4L2_COLORSPACE_SMPTE170M - * - ColorSpace::Rec709 for V4L2_COLORSPACE_REC709 - */ - -CameraConfiguration::Status RPiCameraConfiguration::validateColorSpaces([[maybe_unused]] ColorSpaceFlags flags) -{ - Status status = Valid; - yuvColorSpace_.reset(); - - for (auto cfg : config_) { - /* First fix up raw streams to have the "raw" colour space. */ - if (isRaw(cfg.pixelFormat)) { - /* If there was no value here, that doesn't count as "adjusted". */ - if (cfg.colorSpace && cfg.colorSpace != ColorSpace::Raw) - status = Adjusted; - cfg.colorSpace = ColorSpace::Raw; - continue; - } - - /* Next we need to find our shared colour space. The first valid one will do. */ - if (cfg.colorSpace && !yuvColorSpace_) - yuvColorSpace_ = findValidColorSpace(cfg.colorSpace.value()); - } - - /* If no colour space was given anywhere, choose sYCC. */ - if (!yuvColorSpace_) - yuvColorSpace_ = ColorSpace::Sycc; - - /* Note the version of this that any RGB streams will have to use. */ - rgbColorSpace_ = yuvColorSpace_; - rgbColorSpace_->ycbcrEncoding = ColorSpace::YcbcrEncoding::None; - rgbColorSpace_->range = ColorSpace::Range::Full; - - /* Go through the streams again and force everyone to the same colour space. */ - for (auto cfg : config_) { - if (cfg.colorSpace == ColorSpace::Raw) - continue; - - if (isYuv(cfg.pixelFormat) && cfg.colorSpace != yuvColorSpace_) { - /* Again, no value means "not adjusted". */ - if (cfg.colorSpace) - status = Adjusted; - cfg.colorSpace = yuvColorSpace_; - } - if (isRgb(cfg.pixelFormat) && cfg.colorSpace != rgbColorSpace_) { - /* Be nice, and let the YUV version count as non-adjusted too. */ - if (cfg.colorSpace && cfg.colorSpace != yuvColorSpace_) - status = Adjusted; - cfg.colorSpace = rgbColorSpace_; - } - } - - return status; -} - -CameraConfiguration::Status RPiCameraConfiguration::validate() -{ - Status status = Valid; - - if (config_.empty()) - return Invalid; - - status = validateColorSpaces(ColorSpaceFlag::StreamsShareColorSpace); - - /* - * Validate the requested transform against the sensor capabilities and - * rotation and store the final combined transform that configure() will - * need to apply to the sensor to save us working it out again. - */ - Transform requestedTransform = transform; - combinedTransform_ = data_->sensor_->validateTransform(&transform); - if (transform != requestedTransform) - status = Adjusted; - - unsigned int rawCount = 0, outCount = 0, count = 0, maxIndex = 0; - std::pair outSize[2]; - Size maxSize; - for (StreamConfiguration &cfg : config_) { - if (isRaw(cfg.pixelFormat)) { - /* - * Calculate the best sensor mode we can use based on - * the user request. - */ - V4L2VideoDevice *unicam = data_->unicam_[Unicam::Image].dev(); - const PixelFormatInfo &info = PixelFormatInfo::info(cfg.pixelFormat); - unsigned int bitDepth = info.isValid() ? info.bitsPerPixel : defaultRawBitDepth; - V4L2SubdeviceFormat sensorFormat = findBestFormat(data_->sensorFormats_, cfg.size, bitDepth); - BayerFormat::Packing packing = BayerFormat::Packing::CSI2; - if (info.isValid() && !info.packed) - packing = BayerFormat::Packing::None; - V4L2DeviceFormat unicamFormat = toV4L2DeviceFormat(unicam, sensorFormat, packing); - int ret = unicam->tryFormat(&unicamFormat); - if (ret) - return Invalid; - - /* - * Some sensors change their Bayer order when they are - * h-flipped or v-flipped, according to the transform. - * If this one does, we must advertise the transformed - * Bayer order in the raw stream. Note how we must - * fetch the "native" (i.e. untransformed) Bayer order, - * because the sensor may currently be flipped! - */ - V4L2PixelFormat fourcc = unicamFormat.fourcc; - if (data_->flipsAlterBayerOrder_) { - BayerFormat bayer = BayerFormat::fromV4L2PixelFormat(fourcc); - bayer.order = data_->nativeBayerOrder_; - bayer = bayer.transform(combinedTransform_); - fourcc = bayer.toV4L2PixelFormat(); - } - - PixelFormat unicamPixFormat = fourcc.toPixelFormat(); - if (cfg.size != unicamFormat.size || - cfg.pixelFormat != unicamPixFormat) { - cfg.size = unicamFormat.size; - cfg.pixelFormat = unicamPixFormat; - status = Adjusted; - } - - cfg.stride = unicamFormat.planes[0].bpl; - cfg.frameSize = unicamFormat.planes[0].size; - - rawCount++; - } else { - outSize[outCount] = std::make_pair(count, cfg.size); - /* Record the largest resolution for fixups later. */ - if (maxSize < cfg.size) { - maxSize = cfg.size; - maxIndex = outCount; - } - outCount++; - } - - count++; - - /* Can only output 1 RAW stream, or 2 YUV/RGB streams. */ - if (rawCount > 1 || outCount > 2) { - LOG(RPI, Error) << "Invalid number of streams requested"; - return Invalid; - } - } - - /* - * Now do any fixups needed. For the two ISP outputs, one stream must be - * equal or smaller than the other in all dimensions. - */ - for (unsigned int i = 0; i < outCount; i++) { - outSize[i].second.width = std::min(outSize[i].second.width, - maxSize.width); - outSize[i].second.height = std::min(outSize[i].second.height, - maxSize.height); - - if (config_.at(outSize[i].first).size != outSize[i].second) { - config_.at(outSize[i].first).size = outSize[i].second; - status = Adjusted; - } - - /* - * Also validate the correct pixel formats here. - * Note that Output0 and Output1 support a different - * set of formats. - * - * Output 0 must be for the largest resolution. We will - * have that fixed up in the code above. - * - */ - StreamConfiguration &cfg = config_.at(outSize[i].first); - PixelFormat &cfgPixFmt = cfg.pixelFormat; - V4L2VideoDevice *dev; - - if (i == maxIndex) - dev = data_->isp_[Isp::Output0].dev(); - else - dev = data_->isp_[Isp::Output1].dev(); - - V4L2VideoDevice::Formats fmts = dev->formats(); - - if (fmts.find(dev->toV4L2PixelFormat(cfgPixFmt)) == fmts.end()) { - /* If we cannot find a native format, use a default one. */ - cfgPixFmt = formats::NV12; - status = Adjusted; - } - - V4L2DeviceFormat format; - format.fourcc = dev->toV4L2PixelFormat(cfg.pixelFormat); - format.size = cfg.size; - /* We want to send the associated YCbCr info through to the driver. */ - format.colorSpace = yuvColorSpace_; - - LOG(RPI, Debug) - << "Try color space " << ColorSpace::toString(cfg.colorSpace); - - int ret = dev->tryFormat(&format); - if (ret) - return Invalid; - - /* - * But for RGB streams, the YCbCr info gets overwritten on the way back - * so we must check against what the stream cfg says, not what we actually - * requested (which carefully included the YCbCr info)! - */ - if (cfg.colorSpace != format.colorSpace) { - status = Adjusted; - LOG(RPI, Debug) - << "Color space changed from " - << ColorSpace::toString(cfg.colorSpace) << " to " - << ColorSpace::toString(format.colorSpace); - } - - cfg.colorSpace = format.colorSpace; - - cfg.stride = format.planes[0].bpl; - cfg.frameSize = format.planes[0].size; - - } - - return status; -} - -PipelineHandlerRPi::PipelineHandlerRPi(CameraManager *manager) - : PipelineHandler(manager) -{ -} - -std::unique_ptr -PipelineHandlerRPi::generateConfiguration(Camera *camera, const StreamRoles &roles) -{ - RPiCameraData *data = cameraData(camera); - std::unique_ptr config = - std::make_unique(data); - V4L2SubdeviceFormat sensorFormat; - unsigned int bufferCount; - PixelFormat pixelFormat; - V4L2VideoDevice::Formats fmts; - Size size; - std::optional colorSpace; - - if (roles.empty()) - return config; - - unsigned int rawCount = 0; - unsigned int outCount = 0; - Size sensorSize = data->sensor_->resolution(); - for (const StreamRole role : roles) { - switch (role) { - case StreamRole::Raw: - size = sensorSize; - sensorFormat = findBestFormat(data->sensorFormats_, size, defaultRawBitDepth); - pixelFormat = mbusCodeToPixelFormat(sensorFormat.mbus_code, - BayerFormat::Packing::CSI2); - ASSERT(pixelFormat.isValid()); - colorSpace = ColorSpace::Raw; - bufferCount = 2; - rawCount++; - break; - - case StreamRole::StillCapture: - fmts = data->isp_[Isp::Output0].dev()->formats(); - pixelFormat = formats::NV12; - /* - * Still image codecs usually expect the sYCC color space. - * Even RGB codecs will be fine as the RGB we get with the - * sYCC color space is the same as sRGB. - */ - colorSpace = ColorSpace::Sycc; - /* Return the largest sensor resolution. */ - size = sensorSize; - bufferCount = 1; - outCount++; - break; - - case StreamRole::VideoRecording: - /* - * The colour denoise algorithm requires the analysis - * image, produced by the second ISP output, to be in - * YUV420 format. Select this format as the default, to - * maximize chances that it will be picked by - * applications and enable usage of the colour denoise - * algorithm. - */ - fmts = data->isp_[Isp::Output0].dev()->formats(); - pixelFormat = formats::YUV420; - /* - * Choose a color space appropriate for video recording. - * Rec.709 will be a good default for HD resolutions. - */ - colorSpace = ColorSpace::Rec709; - size = { 1920, 1080 }; - bufferCount = 4; - outCount++; - break; - - case StreamRole::Viewfinder: - fmts = data->isp_[Isp::Output0].dev()->formats(); - pixelFormat = formats::ARGB8888; - colorSpace = ColorSpace::Sycc; - size = { 800, 600 }; - bufferCount = 4; - outCount++; - break; - - default: - LOG(RPI, Error) << "Requested stream role not supported: " - << role; - return nullptr; - } - - if (rawCount > 1 || outCount > 2) { - LOG(RPI, Error) << "Invalid stream roles requested"; - return nullptr; - } - - std::map> deviceFormats; - if (role == StreamRole::Raw) { - /* Translate the MBUS codes to a PixelFormat. */ - for (const auto &format : data->sensorFormats_) { - PixelFormat pf = mbusCodeToPixelFormat(format.first, - BayerFormat::Packing::CSI2); - if (pf.isValid()) - deviceFormats.emplace(std::piecewise_construct, std::forward_as_tuple(pf), - std::forward_as_tuple(format.second.begin(), format.second.end())); - } - } else { - /* - * Translate the V4L2PixelFormat to PixelFormat. Note that we - * limit the recommended largest ISP output size to match the - * sensor resolution. - */ - for (const auto &format : fmts) { - PixelFormat pf = format.first.toPixelFormat(); - if (pf.isValid()) { - const SizeRange &ispSizes = format.second[0]; - deviceFormats[pf].emplace_back(ispSizes.min, sensorSize, - ispSizes.hStep, ispSizes.vStep); - } - } - } - - /* Add the stream format based on the device node used for the use case. */ - StreamFormats formats(deviceFormats); - StreamConfiguration cfg(formats); - cfg.size = size; - cfg.pixelFormat = pixelFormat; - cfg.colorSpace = colorSpace; - cfg.bufferCount = bufferCount; - config->addConfiguration(cfg); - } - - config->validate(); - - return config; -} - -int PipelineHandlerRPi::configure(Camera *camera, CameraConfiguration *config) -{ - RPiCameraData *data = cameraData(camera); - int ret; - - /* Start by freeing all buffers and reset the Unicam and ISP stream states. */ - data->freeBuffers(); - for (auto const stream : data->streams_) - stream->setExternal(false); - - BayerFormat::Packing packing = BayerFormat::Packing::CSI2; - Size maxSize, sensorSize; - unsigned int maxIndex = 0; - bool rawStream = false; - unsigned int bitDepth = defaultRawBitDepth; - - /* - * Look for the RAW stream (if given) size as well as the largest - * ISP output size. - */ - for (unsigned i = 0; i < config->size(); i++) { - StreamConfiguration &cfg = config->at(i); - - if (isRaw(cfg.pixelFormat)) { - /* - * If we have been given a RAW stream, use that size - * for setting up the sensor. - */ - sensorSize = cfg.size; - rawStream = true; - /* Check if the user has explicitly set an unpacked format. */ - BayerFormat bayerFormat = BayerFormat::fromPixelFormat(cfg.pixelFormat); - packing = bayerFormat.packing; - bitDepth = bayerFormat.bitDepth; - } else { - if (cfg.size > maxSize) { - maxSize = config->at(i).size; - maxIndex = i; - } - } - } - - /* - * Calculate the best sensor mode we can use based on the user's - * request, and apply it to the sensor with the cached transform, if - * any. - */ - V4L2SubdeviceFormat sensorFormat = findBestFormat(data->sensorFormats_, rawStream ? sensorSize : maxSize, bitDepth); - const RPiCameraConfiguration *rpiConfig = static_cast(config); - ret = data->sensor_->setFormat(&sensorFormat, rpiConfig->combinedTransform_); - if (ret) - return ret; - - V4L2VideoDevice *unicam = data->unicam_[Unicam::Image].dev(); - V4L2DeviceFormat unicamFormat = toV4L2DeviceFormat(unicam, sensorFormat, packing); - ret = unicam->setFormat(&unicamFormat); - if (ret) - return ret; - - LOG(RPI, Info) << "Sensor: " << camera->id() - << " - Selected sensor format: " << sensorFormat - << " - Selected unicam format: " << unicamFormat; - - ret = data->isp_[Isp::Input].dev()->setFormat(&unicamFormat); - if (ret) - return ret; - - /* - * See which streams are requested, and route the user - * StreamConfiguration appropriately. - */ - V4L2DeviceFormat format; - bool output0Set = false, output1Set = false; - for (unsigned i = 0; i < config->size(); i++) { - StreamConfiguration &cfg = config->at(i); - - if (isRaw(cfg.pixelFormat)) { - cfg.setStream(&data->unicam_[Unicam::Image]); - data->unicam_[Unicam::Image].setExternal(true); - continue; - } - - /* The largest resolution gets routed to the ISP Output 0 node. */ - RPi::Stream *stream = i == maxIndex ? &data->isp_[Isp::Output0] - : &data->isp_[Isp::Output1]; - - V4L2PixelFormat fourcc = stream->dev()->toV4L2PixelFormat(cfg.pixelFormat); - format.size = cfg.size; - format.fourcc = fourcc; - format.colorSpace = cfg.colorSpace; - - LOG(RPI, Debug) << "Setting " << stream->name() << " to " - << format; - - ret = stream->dev()->setFormat(&format); - if (ret) - return -EINVAL; - - if (format.size != cfg.size || format.fourcc != fourcc) { - LOG(RPI, Error) - << "Failed to set requested format on " << stream->name() - << ", returned " << format; - return -EINVAL; - } - - LOG(RPI, Debug) - << "Stream " << stream->name() << " has color space " - << ColorSpace::toString(cfg.colorSpace); - - cfg.setStream(stream); - stream->setExternal(true); - - if (i != maxIndex) - output1Set = true; - else - output0Set = true; - } - - /* - * If ISP::Output0 stream has not been configured by the application, - * we must allow the hardware to generate an output so that the data - * flow in the pipeline handler remains consistent, and we still generate - * statistics for the IPA to use. So enable the output at a very low - * resolution for internal use. - * - * \todo Allow the pipeline to work correctly without Output0 and only - * statistics coming from the hardware. - */ - if (!output0Set) { - V4L2VideoDevice *dev = data->isp_[Isp::Output0].dev(); - - maxSize = Size(320, 240); - format = {}; - format.size = maxSize; - format.fourcc = dev->toV4L2PixelFormat(formats::YUV420); - /* No one asked for output, so the color space doesn't matter. */ - format.colorSpace = ColorSpace::Sycc; - ret = dev->setFormat(&format); - if (ret) { - LOG(RPI, Error) - << "Failed to set default format on ISP Output0: " - << ret; - return -EINVAL; - } - - LOG(RPI, Debug) << "Defaulting ISP Output0 format to " - << format; - } - - /* - * If ISP::Output1 stream has not been requested by the application, we - * set it up for internal use now. This second stream will be used for - * fast colour denoise, and must be a quarter resolution of the ISP::Output0 - * stream. However, also limit the maximum size to 1200 pixels in the - * larger dimension, just to avoid being wasteful with buffer allocations - * and memory bandwidth. - * - * \todo If Output 1 format is not YUV420, Output 1 ought to be disabled as - * colour denoise will not run. - */ - if (!output1Set) { - V4L2VideoDevice *dev = data->isp_[Isp::Output1].dev(); - - V4L2DeviceFormat output1Format; - constexpr Size maxDimensions(1200, 1200); - const Size limit = maxDimensions.boundedToAspectRatio(format.size); - - output1Format.size = (format.size / 2).boundedTo(limit).alignedDownTo(2, 2); - output1Format.colorSpace = format.colorSpace; - output1Format.fourcc = dev->toV4L2PixelFormat(formats::YUV420); - - LOG(RPI, Debug) << "Setting ISP Output1 (internal) to " - << output1Format; - - ret = dev->setFormat(&output1Format); - if (ret) { - LOG(RPI, Error) << "Failed to set format on ISP Output1: " - << ret; - return -EINVAL; - } - } - - /* ISP statistics output format. */ - format = {}; - format.fourcc = V4L2PixelFormat(V4L2_META_FMT_BCM2835_ISP_STATS); - ret = data->isp_[Isp::Stats].dev()->setFormat(&format); - if (ret) { - LOG(RPI, Error) << "Failed to set format on ISP stats stream: " - << format; - return ret; - } - - /* Figure out the smallest selection the ISP will allow. */ - Rectangle testCrop(0, 0, 1, 1); - data->isp_[Isp::Input].dev()->setSelection(V4L2_SEL_TGT_CROP, &testCrop); - data->ispMinCropSize_ = testCrop.size(); - - /* Adjust aspect ratio by providing crops on the input image. */ - Size size = unicamFormat.size.boundedToAspectRatio(maxSize); - Rectangle crop = size.centeredTo(Rectangle(unicamFormat.size).center()); - Rectangle defaultCrop = crop; - data->ispCrop_ = crop; - - data->isp_[Isp::Input].dev()->setSelection(V4L2_SEL_TGT_CROP, &crop); - - ipa::RPi::IPAConfigResult result; - ret = data->configureIPA(config, &result); - if (ret) - LOG(RPI, Error) << "Failed to configure the IPA: " << ret; - - /* - * Set the scaler crop to the value we are using (scaled to native sensor - * coordinates). - */ - data->scalerCrop_ = data->scaleIspCrop(data->ispCrop_); - - /* - * Configure the Unicam embedded data output format only if the sensor - * supports it. - */ - if (data->sensorMetadata_) { - V4L2SubdeviceFormat embeddedFormat; - - data->sensor_->device()->getFormat(1, &embeddedFormat); - format.fourcc = V4L2PixelFormat(V4L2_META_FMT_SENSOR_DATA); - format.planes[0].size = embeddedFormat.size.width * embeddedFormat.size.height; - - LOG(RPI, Debug) << "Setting embedded data format."; - ret = data->unicam_[Unicam::Embedded].dev()->setFormat(&format); - if (ret) { - LOG(RPI, Error) << "Failed to set format on Unicam embedded: " - << format; - return ret; - } - } - - /* - * Update the ScalerCropMaximum to the correct value for this camera mode. - * For us, it's the same as the "analogue crop". - * - * \todo Make this property the ScalerCrop maximum value when dynamic - * controls are available and set it at validate() time - */ - data->properties_.set(properties::ScalerCropMaximum, data->sensorInfo_.analogCrop); - - /* Store the mode sensitivity for the application. */ - data->properties_.set(properties::SensorSensitivity, result.modeSensitivity); - - /* Update the controls that the Raspberry Pi IPA can handle. */ - ControlInfoMap::Map ctrlMap; - for (auto const &c : result.controlInfo) - ctrlMap.emplace(c.first, c.second); - - /* Add the ScalerCrop control limits based on the current mode. */ - Rectangle ispMinCrop = data->scaleIspCrop(Rectangle(data->ispMinCropSize_)); - defaultCrop = data->scaleIspCrop(defaultCrop); - ctrlMap[&controls::ScalerCrop] = ControlInfo(ispMinCrop, data->sensorInfo_.analogCrop, defaultCrop); - - data->controlInfo_ = ControlInfoMap(std::move(ctrlMap), result.controlInfo.idmap()); - - /* Setup the Video Mux/Bridge entities. */ - for (auto &[device, link] : data->bridgeDevices_) { - /* - * Start by disabling all the sink pad links on the devices in the - * cascade, with the exception of the link connecting the device. - */ - for (const MediaPad *p : device->entity()->pads()) { - if (!(p->flags() & MEDIA_PAD_FL_SINK)) - continue; - - for (MediaLink *l : p->links()) { - if (l != link) - l->setEnabled(false); - } - } - - /* - * Next, enable the entity -> entity links, and setup the pad format. - * - * \todo Some bridge devices may chainge the media bus code, so we - * ought to read the source pad format and propagate it to the sink pad. - */ - link->setEnabled(true); - const MediaPad *sinkPad = link->sink(); - ret = device->setFormat(sinkPad->index(), &sensorFormat); - if (ret) { - LOG(RPI, Error) << "Failed to set format on " << device->entity()->name() - << " pad " << sinkPad->index() - << " with format " << format - << ": " << ret; - return ret; - } - - LOG(RPI, Debug) << "Configured media link on device " << device->entity()->name() - << " on pad " << sinkPad->index(); - } - - return ret; -} - -int PipelineHandlerRPi::exportFrameBuffers([[maybe_unused]] Camera *camera, Stream *stream, - std::vector> *buffers) -{ - RPi::Stream *s = static_cast(stream); - unsigned int count = stream->configuration().bufferCount; - int ret = s->dev()->exportBuffers(count, buffers); - - s->setExportedBuffers(buffers); - - return ret; -} - -int PipelineHandlerRPi::start(Camera *camera, const ControlList *controls) -{ - RPiCameraData *data = cameraData(camera); - int ret; - - /* Check if a ScalerCrop control was specified. */ - if (controls) - data->applyScalerCrop(*controls); - - /* Start the IPA. */ - ipa::RPi::StartConfig startConfig; - data->ipa_->start(controls ? *controls : ControlList{ controls::controls }, - &startConfig); - - /* Apply any gain/exposure settings that the IPA may have passed back. */ - if (!startConfig.controls.empty()) - data->setSensorControls(startConfig.controls); - - /* Configure the number of dropped frames required on startup. */ - data->dropFrameCount_ = data->config_.disableStartupFrameDrops - ? 0 : startConfig.dropFrameCount; - - for (auto const stream : data->streams_) - stream->resetBuffers(); - - if (!data->buffersAllocated_) { - /* Allocate buffers for internal pipeline usage. */ - ret = prepareBuffers(camera); - if (ret) { - LOG(RPI, Error) << "Failed to allocate buffers"; - data->freeBuffers(); - stop(camera); - return ret; - } - data->buffersAllocated_ = true; - } - - /* We need to set the dropFrameCount_ before queueing buffers. */ - ret = queueAllBuffers(camera); - if (ret) { - LOG(RPI, Error) << "Failed to queue buffers"; - stop(camera); - return ret; - } - - /* Enable SOF event generation. */ - data->unicam_[Unicam::Image].dev()->setFrameStartEnabled(true); - - /* - * Reset the delayed controls with the gain and exposure values set by - * the IPA. - */ - data->delayedCtrls_->reset(0); - - data->state_ = RPiCameraData::State::Idle; - - /* Start all streams. */ - for (auto const stream : data->streams_) { - ret = stream->dev()->streamOn(); - if (ret) { - stop(camera); - return ret; - } - } - - return 0; -} - -void PipelineHandlerRPi::stopDevice(Camera *camera) -{ - RPiCameraData *data = cameraData(camera); - - data->state_ = RPiCameraData::State::Stopped; - - /* Disable SOF event generation. */ - data->unicam_[Unicam::Image].dev()->setFrameStartEnabled(false); - - for (auto const stream : data->streams_) - stream->dev()->streamOff(); - - data->clearIncompleteRequests(); - data->bayerQueue_ = {}; - data->embeddedQueue_ = {}; - - /* Stop the IPA. */ - data->ipa_->stop(); -} - -int PipelineHandlerRPi::queueRequestDevice(Camera *camera, Request *request) -{ - RPiCameraData *data = cameraData(camera); - - if (!data->isRunning()) - return -EINVAL; - - LOG(RPI, Debug) << "queueRequestDevice: New request."; - - /* Push all buffers supplied in the Request to the respective streams. */ - for (auto stream : data->streams_) { - if (!stream->isExternal()) - continue; - - FrameBuffer *buffer = request->findBuffer(stream); - if (buffer && stream->getBufferId(buffer) == -1) { - /* - * This buffer is not recognised, so it must have been allocated - * outside the v4l2 device. Store it in the stream buffer list - * so we can track it. - */ - stream->setExternalBuffer(buffer); - } - - /* - * If no buffer is provided by the request for this stream, we - * queue a nullptr to the stream to signify that it must use an - * internally allocated buffer for this capture request. This - * buffer will not be given back to the application, but is used - * to support the internal pipeline flow. - * - * The below queueBuffer() call will do nothing if there are not - * enough internal buffers allocated, but this will be handled by - * queuing the request for buffers in the RPiStream object. - */ - int ret = stream->queueBuffer(buffer); - if (ret) - return ret; - } - - /* Push the request to the back of the queue. */ - data->requestQueue_.push_back(request); - data->handleState(); - - return 0; -} - -bool PipelineHandlerRPi::match(DeviceEnumerator *enumerator) -{ - constexpr unsigned int numUnicamDevices = 2; - - /* - * Loop over all Unicam instances, but return out once a match is found. - * This is to ensure we correctly enumrate the camera when an instance - * of Unicam has registered with media controller, but has not registered - * device nodes due to a sensor subdevice failure. - */ - for (unsigned int i = 0; i < numUnicamDevices; i++) { - DeviceMatch unicam("unicam"); - MediaDevice *unicamDevice = acquireMediaDevice(enumerator, unicam); - - if (!unicamDevice) { - LOG(RPI, Debug) << "Unable to acquire a Unicam instance"; - continue; - } - - DeviceMatch isp("bcm2835-isp"); - MediaDevice *ispDevice = acquireMediaDevice(enumerator, isp); - - if (!ispDevice) { - LOG(RPI, Debug) << "Unable to acquire ISP instance"; - continue; - } - - /* - * The loop below is used to register multiple cameras behind one or more - * video mux devices that are attached to a particular Unicam instance. - * Obviously these cameras cannot be used simultaneously. - */ - unsigned int numCameras = 0; - for (MediaEntity *entity : unicamDevice->entities()) { - if (entity->function() != MEDIA_ENT_F_CAM_SENSOR) - continue; - - int ret = registerCamera(unicamDevice, ispDevice, entity); - if (ret) - LOG(RPI, Error) << "Failed to register camera " - << entity->name() << ": " << ret; - else - numCameras++; - } - - if (numCameras) - return true; - } - - return false; -} - -void PipelineHandlerRPi::releaseDevice(Camera *camera) -{ - RPiCameraData *data = cameraData(camera); - data->freeBuffers(); -} - -int PipelineHandlerRPi::registerCamera(MediaDevice *unicam, MediaDevice *isp, MediaEntity *sensorEntity) -{ - std::unique_ptr data = std::make_unique(this); - - if (!data->dmaHeap_.isValid()) - return -ENOMEM; - - MediaEntity *unicamImage = unicam->getEntityByName("unicam-image"); - MediaEntity *ispOutput0 = isp->getEntityByName("bcm2835-isp0-output0"); - MediaEntity *ispCapture1 = isp->getEntityByName("bcm2835-isp0-capture1"); - MediaEntity *ispCapture2 = isp->getEntityByName("bcm2835-isp0-capture2"); - MediaEntity *ispCapture3 = isp->getEntityByName("bcm2835-isp0-capture3"); - - if (!unicamImage || !ispOutput0 || !ispCapture1 || !ispCapture2 || !ispCapture3) - return -ENOENT; - - /* Locate and open the unicam video streams. */ - data->unicam_[Unicam::Image] = RPi::Stream("Unicam Image", unicamImage); - - /* An embedded data node will not be present if the sensor does not support it. */ - MediaEntity *unicamEmbedded = unicam->getEntityByName("unicam-embedded"); - if (unicamEmbedded) { - data->unicam_[Unicam::Embedded] = RPi::Stream("Unicam Embedded", unicamEmbedded); - data->unicam_[Unicam::Embedded].dev()->bufferReady.connect(data.get(), - &RPiCameraData::unicamBufferDequeue); - } - - /* Tag the ISP input stream as an import stream. */ - data->isp_[Isp::Input] = RPi::Stream("ISP Input", ispOutput0, true); - data->isp_[Isp::Output0] = RPi::Stream("ISP Output0", ispCapture1); - data->isp_[Isp::Output1] = RPi::Stream("ISP Output1", ispCapture2); - data->isp_[Isp::Stats] = RPi::Stream("ISP Stats", ispCapture3); - - /* Wire up all the buffer connections. */ - data->unicam_[Unicam::Image].dev()->dequeueTimeout.connect(data.get(), &RPiCameraData::unicamTimeout); - data->unicam_[Unicam::Image].dev()->frameStart.connect(data.get(), &RPiCameraData::frameStarted); - data->unicam_[Unicam::Image].dev()->bufferReady.connect(data.get(), &RPiCameraData::unicamBufferDequeue); - data->isp_[Isp::Input].dev()->bufferReady.connect(data.get(), &RPiCameraData::ispInputDequeue); - data->isp_[Isp::Output0].dev()->bufferReady.connect(data.get(), &RPiCameraData::ispOutputDequeue); - data->isp_[Isp::Output1].dev()->bufferReady.connect(data.get(), &RPiCameraData::ispOutputDequeue); - data->isp_[Isp::Stats].dev()->bufferReady.connect(data.get(), &RPiCameraData::ispOutputDequeue); - - data->sensor_ = std::make_unique(sensorEntity); - if (!data->sensor_) - return -EINVAL; - - if (data->sensor_->init()) - return -EINVAL; - - /* - * Enumerate all the Video Mux/Bridge devices across the sensor -> unicam - * chain. There may be a cascade of devices in this chain! - */ - MediaLink *link = sensorEntity->getPadByIndex(0)->links()[0]; - data->enumerateVideoDevices(link); - - data->sensorFormats_ = populateSensorFormats(data->sensor_); - - ipa::RPi::IPAInitResult result; - if (data->loadIPA(&result)) { - LOG(RPI, Error) << "Failed to load a suitable IPA library"; - return -EINVAL; - } - - if (result.sensorConfig.sensorMetadata ^ !!unicamEmbedded) { - LOG(RPI, Warning) << "Mismatch between Unicam and CamHelper for embedded data usage!"; - result.sensorConfig.sensorMetadata = false; - if (unicamEmbedded) - data->unicam_[Unicam::Embedded].dev()->bufferReady.disconnect(); - } - - /* - * Open all Unicam and ISP streams. The exception is the embedded data - * stream, which only gets opened below if the IPA reports that the sensor - * supports embedded data. - * - * The below grouping is just for convenience so that we can easily - * iterate over all streams in one go. - */ - data->streams_.push_back(&data->unicam_[Unicam::Image]); - if (result.sensorConfig.sensorMetadata) - data->streams_.push_back(&data->unicam_[Unicam::Embedded]); - - for (auto &stream : data->isp_) - data->streams_.push_back(&stream); - - for (auto stream : data->streams_) { - int ret = stream->dev()->open(); - if (ret) - return ret; - } - - if (!data->unicam_[Unicam::Image].dev()->caps().hasMediaController()) { - LOG(RPI, Error) << "Unicam driver does not use the MediaController, please update your kernel!"; - return -EINVAL; - } - - /* - * Setup our delayed control writer with the sensor default - * gain and exposure delays. Mark VBLANK for priority write. - */ - std::unordered_map params = { - { V4L2_CID_ANALOGUE_GAIN, { result.sensorConfig.gainDelay, false } }, - { V4L2_CID_EXPOSURE, { result.sensorConfig.exposureDelay, false } }, - { V4L2_CID_HBLANK, { result.sensorConfig.hblankDelay, false } }, - { V4L2_CID_VBLANK, { result.sensorConfig.vblankDelay, true } } - }; - data->delayedCtrls_ = std::make_unique(data->sensor_->device(), params); - data->sensorMetadata_ = result.sensorConfig.sensorMetadata; - - /* Register initial controls that the Raspberry Pi IPA can handle. */ - data->controlInfo_ = std::move(result.controlInfo); - - /* Initialize the camera properties. */ - data->properties_ = data->sensor_->properties(); - - /* - * The V4L2_CID_NOTIFY_GAINS control, if present, is used to inform the - * sensor of the colour gains. It is defined to be a linear gain where - * the default value represents a gain of exactly one. - */ - auto it = data->sensor_->controls().find(V4L2_CID_NOTIFY_GAINS); - if (it != data->sensor_->controls().end()) - data->notifyGainsUnity_ = it->second.def().get(); - - /* - * Set a default value for the ScalerCropMaximum property to show - * that we support its use, however, initialise it to zero because - * it's not meaningful until a camera mode has been chosen. - */ - data->properties_.set(properties::ScalerCropMaximum, Rectangle{}); - - /* - * We cache two things about the sensor in relation to transforms - * (meaning horizontal and vertical flips): if they affect the Bayer - * ordering, and what the "native" Bayer order is, when no transforms - * are applied. - * - * We note that the sensor's cached list of supported formats is - * already in the "native" order, with any flips having been undone. - */ - const V4L2Subdevice *sensor = data->sensor_->device(); - const struct v4l2_query_ext_ctrl *hflipCtrl = sensor->controlInfo(V4L2_CID_HFLIP); - if (hflipCtrl) { - /* We assume it will support vflips too... */ - data->flipsAlterBayerOrder_ = hflipCtrl->flags & V4L2_CTRL_FLAG_MODIFY_LAYOUT; - } - - /* Look for a valid Bayer format. */ - BayerFormat bayerFormat; - for (const auto &iter : data->sensorFormats_) { - bayerFormat = BayerFormat::fromMbusCode(iter.first); - if (bayerFormat.isValid()) - break; - } - - if (!bayerFormat.isValid()) { - LOG(RPI, Error) << "No Bayer format found"; - return -EINVAL; - } - data->nativeBayerOrder_ = bayerFormat.order; - - /* - * List the available streams an application may request. At present, we - * do not advertise Unicam Embedded and ISP Statistics streams, as there - * is no mechanism for the application to request non-image buffer formats. - */ - std::set streams; - streams.insert(&data->unicam_[Unicam::Image]); - streams.insert(&data->isp_[Isp::Output0]); - streams.insert(&data->isp_[Isp::Output1]); - - int ret = data->loadPipelineConfiguration(); - if (ret) { - LOG(RPI, Error) << "Unable to load pipeline configuration"; - return ret; - } - - /* Create and register the camera. */ - const std::string &id = data->sensor_->id(); - std::shared_ptr camera = - Camera::create(std::move(data), id, streams); - PipelineHandler::registerCamera(std::move(camera)); - - LOG(RPI, Info) << "Registered camera " << id - << " to Unicam device " << unicam->deviceNode() - << " and ISP device " << isp->deviceNode(); - return 0; -} - -int PipelineHandlerRPi::queueAllBuffers(Camera *camera) -{ - RPiCameraData *data = cameraData(camera); - int ret; - - for (auto const stream : data->streams_) { - if (!stream->isExternal()) { - ret = stream->queueAllBuffers(); - if (ret < 0) - return ret; - } else { - /* - * For external streams, we must queue up a set of internal - * buffers to handle the number of drop frames requested by - * the IPA. This is done by passing nullptr in queueBuffer(). - * - * The below queueBuffer() call will do nothing if there - * are not enough internal buffers allocated, but this will - * be handled by queuing the request for buffers in the - * RPiStream object. - */ - unsigned int i; - for (i = 0; i < data->dropFrameCount_; i++) { - ret = stream->queueBuffer(nullptr); - if (ret) - return ret; - } - } - } - - return 0; -} - -int PipelineHandlerRPi::prepareBuffers(Camera *camera) -{ - RPiCameraData *data = cameraData(camera); - unsigned int numRawBuffers = 0; - int ret; - - for (Stream *s : camera->streams()) { - if (isRaw(s->configuration().pixelFormat)) { - numRawBuffers = s->configuration().bufferCount; - break; - } - } - - /* Decide how many internal buffers to allocate. */ - for (auto const stream : data->streams_) { - unsigned int numBuffers; - /* - * For Unicam, allocate a minimum number of buffers for internal - * use as we want to avoid any frame drops. - */ - const unsigned int minBuffers = data->config_.minTotalUnicamBuffers; - if (stream == &data->unicam_[Unicam::Image]) { - /* - * If an application has configured a RAW stream, allocate - * additional buffers to make up the minimum, but ensure - * we have at least minUnicamBuffers of internal buffers - * to use to minimise frame drops. - */ - numBuffers = std::max(data->config_.minUnicamBuffers, - minBuffers - numRawBuffers); - } else if (stream == &data->isp_[Isp::Input]) { - /* - * ISP input buffers are imported from Unicam, so follow - * similar logic as above to count all the RAW buffers - * available. - */ - numBuffers = numRawBuffers + - std::max(data->config_.minUnicamBuffers, - minBuffers - numRawBuffers); - - } else if (stream == &data->unicam_[Unicam::Embedded]) { - /* - * Embedded data buffers are (currently) for internal use, - * so allocate the minimum required to avoid frame drops. - */ - numBuffers = minBuffers; - } else { - /* - * Since the ISP runs synchronous with the IPA and requests, - * we only ever need one set of internal buffers. Any buffers - * the application wants to hold onto will already be exported - * through PipelineHandlerRPi::exportFrameBuffers(). - */ - numBuffers = 1; - } - - ret = stream->prepareBuffers(numBuffers); - if (ret < 0) - return ret; - } - - /* - * Pass the stats and embedded data buffers to the IPA. No other - * buffers need to be passed. - */ - mapBuffers(camera, data->isp_[Isp::Stats].getBuffers(), RPi::MaskStats); - if (data->sensorMetadata_) - mapBuffers(camera, data->unicam_[Unicam::Embedded].getBuffers(), - RPi::MaskEmbeddedData); - - return 0; -} - -void PipelineHandlerRPi::mapBuffers(Camera *camera, const RPi::BufferMap &buffers, unsigned int mask) -{ - RPiCameraData *data = cameraData(camera); - std::vector ipaBuffers; - /* - * Link the FrameBuffers with the id (key value) in the map stored in - * the RPi stream object - along with an identifier mask. - * - * This will allow us to identify buffers passed between the pipeline - * handler and the IPA. - */ - for (auto const &it : buffers) { - ipaBuffers.push_back(IPABuffer(mask | it.first, - it.second->planes())); - data->ipaBuffers_.insert(mask | it.first); - } - - data->ipa_->mapBuffers(ipaBuffers); -} - -void RPiCameraData::freeBuffers() -{ - if (ipa_) { - /* - * Copy the buffer ids from the unordered_set to a vector to - * pass to the IPA. - */ - std::vector ipaBuffers(ipaBuffers_.begin(), - ipaBuffers_.end()); - ipa_->unmapBuffers(ipaBuffers); - ipaBuffers_.clear(); - } - - for (auto const stream : streams_) - stream->releaseBuffers(); - - buffersAllocated_ = false; -} - -void RPiCameraData::frameStarted(uint32_t sequence) -{ - LOG(RPI, Debug) << "frame start " << sequence; - - /* Write any controls for the next frame as soon as we can. */ - delayedCtrls_->applyControls(sequence); -} - -int RPiCameraData::loadIPA(ipa::RPi::IPAInitResult *result) -{ - ipa_ = IPAManager::createIPA(pipe(), 1, 1); - - if (!ipa_) - return -ENOENT; - - ipa_->statsMetadataComplete.connect(this, &RPiCameraData::statsMetadataComplete); - ipa_->runIsp.connect(this, &RPiCameraData::runIsp); - ipa_->embeddedComplete.connect(this, &RPiCameraData::embeddedComplete); - ipa_->setIspControls.connect(this, &RPiCameraData::setIspControls); - ipa_->setDelayedControls.connect(this, &RPiCameraData::setDelayedControls); - ipa_->setLensControls.connect(this, &RPiCameraData::setLensControls); - ipa_->setCameraTimeout.connect(this, &RPiCameraData::setCameraTimeout); - - /* - * The configuration (tuning file) is made from the sensor name unless - * the environment variable overrides it. - */ - std::string configurationFile; - char const *configFromEnv = utils::secure_getenv("LIBCAMERA_RPI_TUNING_FILE"); - if (!configFromEnv || *configFromEnv == '\0') { - std::string model = sensor_->model(); - if (isMonoSensor(sensor_)) - model += "_mono"; - configurationFile = ipa_->configurationFile(model + ".json"); - } else { - configurationFile = std::string(configFromEnv); - } - - IPASettings settings(configurationFile, sensor_->model()); - - return ipa_->init(settings, !!sensor_->focusLens(), result); -} - -int RPiCameraData::configureIPA(const CameraConfiguration *config, ipa::RPi::IPAConfigResult *result) -{ - std::map entityControls; - ipa::RPi::IPAConfig ipaConfig; - - /* \todo Move passing of ispControls and lensControls to ipa::init() */ - ipaConfig.sensorControls = sensor_->controls(); - ipaConfig.ispControls = isp_[Isp::Input].dev()->controls(); - if (sensor_->focusLens()) - ipaConfig.lensControls = sensor_->focusLens()->controls(); - - /* Always send the user transform to the IPA. */ - ipaConfig.transform = static_cast(config->transform); - - /* Allocate the lens shading table via dmaHeap and pass to the IPA. */ - if (!lsTable_.isValid()) { - lsTable_ = SharedFD(dmaHeap_.alloc("ls_grid", ipa::RPi::MaxLsGridSize)); - if (!lsTable_.isValid()) - return -ENOMEM; - - /* Allow the IPA to mmap the LS table via the file descriptor. */ - /* - * \todo Investigate if mapping the lens shading table buffer - * could be handled with mapBuffers(). - */ - ipaConfig.lsTableHandle = lsTable_; - } - - /* We store the IPACameraSensorInfo for digital zoom calculations. */ - int ret = sensor_->sensorInfo(&sensorInfo_); - if (ret) { - LOG(RPI, Error) << "Failed to retrieve camera sensor info"; - return ret; - } - - /* Ready the IPA - it must know about the sensor resolution. */ - ControlList controls; - ret = ipa_->configure(sensorInfo_, ipaConfig, &controls, result); - if (ret < 0) { - LOG(RPI, Error) << "IPA configuration failed!"; - return -EPIPE; - } - - if (!controls.empty()) - setSensorControls(controls); - - return 0; -} - -int RPiCameraData::loadPipelineConfiguration() -{ - config_ = { - .minUnicamBuffers = 2, - .minTotalUnicamBuffers = 4, - .disableStartupFrameDrops = false, - .unicamTimeoutValue = 0, - }; - - char const *configFromEnv = utils::secure_getenv("LIBCAMERA_RPI_CONFIG_FILE"); - if (!configFromEnv || *configFromEnv == '\0') - return 0; - - std::string filename = std::string(configFromEnv); - File file(filename); - - if (!file.open(File::OpenModeFlag::ReadOnly)) { - LOG(RPI, Error) << "Failed to open configuration file '" << filename << "'"; - return -EIO; - } - - LOG(RPI, Info) << "Using configuration file '" << filename << "'"; - - std::unique_ptr root = YamlParser::parse(file); - if (!root) { - LOG(RPI, Warning) << "Failed to parse configuration file, using defaults"; - return 0; - } - - std::optional ver = (*root)["version"].get(); - if (!ver || *ver != 1.0) { - LOG(RPI, Error) << "Unexpected configuration file version reported"; - return -EINVAL; - } - - const YamlObject &phConfig = (*root)["pipeline_handler"]; - config_.minUnicamBuffers = - phConfig["min_unicam_buffers"].get(config_.minUnicamBuffers); - config_.minTotalUnicamBuffers = - phConfig["min_total_unicam_buffers"].get(config_.minTotalUnicamBuffers); - config_.disableStartupFrameDrops = - phConfig["disable_startup_frame_drops"].get(config_.disableStartupFrameDrops); - config_.unicamTimeoutValue = - phConfig["unicam_timeout_value_ms"].get(config_.unicamTimeoutValue); - - if (config_.unicamTimeoutValue) { - /* Disable the IPA signal to control timeout and set the user requested value. */ - ipa_->setCameraTimeout.disconnect(); - unicam_[Unicam::Image].dev()->setDequeueTimeout(config_.unicamTimeoutValue * 1ms); - } - - if (config_.minTotalUnicamBuffers < config_.minUnicamBuffers) { - LOG(RPI, Error) << "Invalid configuration: min_total_unicam_buffers must be >= min_unicam_buffers"; - return -EINVAL; - } - - if (config_.minTotalUnicamBuffers < 1) { - LOG(RPI, Error) << "Invalid configuration: min_total_unicam_buffers must be >= 1"; - return -EINVAL; - } - - return 0; -} - -/* - * enumerateVideoDevices() iterates over the Media Controller topology, starting - * at the sensor and finishing at Unicam. For each sensor, RPiCameraData stores - * a unique list of any intermediate video mux or bridge devices connected in a - * cascade, together with the entity to entity link. - * - * Entity pad configuration and link enabling happens at the end of configure(). - * We first disable all pad links on each entity device in the chain, and then - * selectively enabling the specific links to link sensor to Unicam across all - * intermediate muxes and bridges. - * - * In the cascaded topology below, if Sensor1 is used, the Mux2 -> Mux1 link - * will be disabled, and Sensor1 -> Mux1 -> Unicam links enabled. Alternatively, - * if Sensor3 is used, the Sensor2 -> Mux2 and Sensor1 -> Mux1 links are disabled, - * and Sensor3 -> Mux2 -> Mux1 -> Unicam links are enabled. All other links will - * remain unchanged. - * - * +----------+ - * | Unicam | - * +-----^----+ - * | - * +---+---+ - * | Mux1 <-------+ - * +--^----+ | - * | | - * +-----+---+ +---+---+ - * | Sensor1 | | Mux2 |<--+ - * +---------+ +-^-----+ | - * | | - * +-------+-+ +---+-----+ - * | Sensor2 | | Sensor3 | - * +---------+ +---------+ - */ -void RPiCameraData::enumerateVideoDevices(MediaLink *link) -{ - const MediaPad *sinkPad = link->sink(); - const MediaEntity *entity = sinkPad->entity(); - bool unicamFound = false; - - /* We only deal with Video Mux and Bridge devices in cascade. */ - if (entity->function() != MEDIA_ENT_F_VID_MUX && - entity->function() != MEDIA_ENT_F_VID_IF_BRIDGE) - return; - - /* Find the source pad for this Video Mux or Bridge device. */ - const MediaPad *sourcePad = nullptr; - for (const MediaPad *pad : entity->pads()) { - if (pad->flags() & MEDIA_PAD_FL_SOURCE) { - /* - * We can only deal with devices that have a single source - * pad. If this device has multiple source pads, ignore it - * and this branch in the cascade. - */ - if (sourcePad) - return; - - sourcePad = pad; - } - } - - LOG(RPI, Debug) << "Found video mux device " << entity->name() - << " linked to sink pad " << sinkPad->index(); - - bridgeDevices_.emplace_back(std::make_unique(entity), link); - bridgeDevices_.back().first->open(); - - /* - * Iterate through all the sink pad links down the cascade to find any - * other Video Mux and Bridge devices. - */ - for (MediaLink *l : sourcePad->links()) { - enumerateVideoDevices(l); - /* Once we reach the Unicam entity, we are done. */ - if (l->sink()->entity()->name() == "unicam-image") { - unicamFound = true; - break; - } - } - - /* This identifies the end of our entity enumeration recursion. */ - if (link->source()->entity()->function() == MEDIA_ENT_F_CAM_SENSOR) { - /* - * If Unicam is not at the end of this cascade, we cannot configure - * this topology automatically, so remove all entity references. - */ - if (!unicamFound) { - LOG(RPI, Warning) << "Cannot automatically configure this MC topology!"; - bridgeDevices_.clear(); - } - } -} - -void RPiCameraData::statsMetadataComplete(uint32_t bufferId, const ControlList &controls) -{ - if (!isRunning()) - return; - - FrameBuffer *buffer = isp_[Isp::Stats].getBuffers().at(bufferId & RPi::MaskID); - - handleStreamBuffer(buffer, &isp_[Isp::Stats]); - - /* Add to the Request metadata buffer what the IPA has provided. */ - Request *request = requestQueue_.front(); - request->metadata().merge(controls); - - /* - * Inform the sensor of the latest colour gains if it has the - * V4L2_CID_NOTIFY_GAINS control (which means notifyGainsUnity_ is set). - */ - const auto &colourGains = controls.get(libcamera::controls::ColourGains); - if (notifyGainsUnity_ && colourGains) { - /* The control wants linear gains in the order B, Gb, Gr, R. */ - ControlList ctrls(sensor_->controls()); - std::array gains{ - static_cast((*colourGains)[1] * *notifyGainsUnity_), - *notifyGainsUnity_, - *notifyGainsUnity_, - static_cast((*colourGains)[0] * *notifyGainsUnity_) - }; - ctrls.set(V4L2_CID_NOTIFY_GAINS, Span{ gains }); - - sensor_->setControls(&ctrls); - } - - state_ = State::IpaComplete; - handleState(); -} - -void RPiCameraData::runIsp(uint32_t bufferId) -{ - if (!isRunning()) - return; - - FrameBuffer *buffer = unicam_[Unicam::Image].getBuffers().at(bufferId & RPi::MaskID); - - LOG(RPI, Debug) << "Input re-queue to ISP, buffer id " << (bufferId & RPi::MaskID) - << ", timestamp: " << buffer->metadata().timestamp; - - isp_[Isp::Input].queueBuffer(buffer); - ispOutputCount_ = 0; - handleState(); -} - -void RPiCameraData::embeddedComplete(uint32_t bufferId) -{ - if (!isRunning()) - return; - - FrameBuffer *buffer = unicam_[Unicam::Embedded].getBuffers().at(bufferId & RPi::MaskID); - handleStreamBuffer(buffer, &unicam_[Unicam::Embedded]); - handleState(); -} - -void RPiCameraData::setIspControls(const ControlList &controls) -{ - ControlList ctrls = controls; - - if (ctrls.contains(V4L2_CID_USER_BCM2835_ISP_LENS_SHADING)) { - ControlValue &value = - const_cast(ctrls.get(V4L2_CID_USER_BCM2835_ISP_LENS_SHADING)); - Span s = value.data(); - bcm2835_isp_lens_shading *ls = - reinterpret_cast(s.data()); - ls->dmabuf = lsTable_.get(); - } - - isp_[Isp::Input].dev()->setControls(&ctrls); - handleState(); -} - -void RPiCameraData::setDelayedControls(const ControlList &controls, uint32_t delayContext) -{ - if (!delayedCtrls_->push(controls, delayContext)) - LOG(RPI, Error) << "V4L2 DelayedControl set failed"; - handleState(); -} - -void RPiCameraData::setLensControls(const ControlList &controls) -{ - CameraLens *lens = sensor_->focusLens(); - - if (lens && controls.contains(V4L2_CID_FOCUS_ABSOLUTE)) { - ControlValue const &focusValue = controls.get(V4L2_CID_FOCUS_ABSOLUTE); - lens->setFocusPosition(focusValue.get()); - } -} - -void RPiCameraData::setCameraTimeout(uint32_t maxFrameLengthMs) -{ - /* - * Set the dequeue timeout to the larger of 5x the maximum reported - * frame length advertised by the IPA over a number of frames. Allow - * a minimum timeout value of 1s. - */ - utils::Duration timeout = - std::max(1s, 5 * maxFrameLengthMs * 1ms); - - LOG(RPI, Debug) << "Setting Unicam timeout to " << timeout; - unicam_[Unicam::Image].dev()->setDequeueTimeout(timeout); -} - -void RPiCameraData::setSensorControls(ControlList &controls) -{ - /* - * We need to ensure that if both VBLANK and EXPOSURE are present, the - * former must be written ahead of, and separately from EXPOSURE to avoid - * V4L2 rejecting the latter. This is identical to what DelayedControls - * does with the priority write flag. - * - * As a consequence of the below logic, VBLANK gets set twice, and we - * rely on the v4l2 framework to not pass the second control set to the - * driver as the actual control value has not changed. - */ - if (controls.contains(V4L2_CID_EXPOSURE) && controls.contains(V4L2_CID_VBLANK)) { - ControlList vblank_ctrl; - - vblank_ctrl.set(V4L2_CID_VBLANK, controls.get(V4L2_CID_VBLANK)); - sensor_->setControls(&vblank_ctrl); - } - - sensor_->setControls(&controls); -} - -void RPiCameraData::unicamTimeout() -{ - LOG(RPI, Error) << "Unicam has timed out!"; - LOG(RPI, Error) << "Please check that your camera sensor connector is attached securely."; - LOG(RPI, Error) << "Alternatively, try another cable and/or sensor."; - - state_ = RPiCameraData::State::Error; - /* - * To allow the application to attempt a recovery from this timeout, - * stop all devices streaming, and return any outstanding requests as - * incomplete and cancelled. - */ - for (auto const stream : streams_) - stream->dev()->streamOff(); - - clearIncompleteRequests(); -} - -void RPiCameraData::unicamBufferDequeue(FrameBuffer *buffer) -{ - RPi::Stream *stream = nullptr; - int index; - - if (!isRunning()) - return; - - for (RPi::Stream &s : unicam_) { - index = s.getBufferId(buffer); - if (index != -1) { - stream = &s; - break; - } - } - - /* The buffer must belong to one of our streams. */ - ASSERT(stream); - - LOG(RPI, Debug) << "Stream " << stream->name() << " buffer dequeue" - << ", buffer id " << index - << ", timestamp: " << buffer->metadata().timestamp; - - if (stream == &unicam_[Unicam::Image]) { - /* - * Lookup the sensor controls used for this frame sequence from - * DelayedControl and queue them along with the frame buffer. - */ - auto [ctrl, delayContext] = delayedCtrls_->get(buffer->metadata().sequence); - /* - * Add the frame timestamp to the ControlList for the IPA to use - * as it does not receive the FrameBuffer object. - */ - ctrl.set(controls::SensorTimestamp, buffer->metadata().timestamp); - bayerQueue_.push({ buffer, std::move(ctrl), delayContext }); - } else { - embeddedQueue_.push(buffer); - } - - handleState(); -} - -void RPiCameraData::ispInputDequeue(FrameBuffer *buffer) -{ - if (!isRunning()) - return; - - LOG(RPI, Debug) << "Stream ISP Input buffer complete" - << ", buffer id " << unicam_[Unicam::Image].getBufferId(buffer) - << ", timestamp: " << buffer->metadata().timestamp; - - /* The ISP input buffer gets re-queued into Unicam. */ - handleStreamBuffer(buffer, &unicam_[Unicam::Image]); - handleState(); -} - -void RPiCameraData::ispOutputDequeue(FrameBuffer *buffer) -{ - RPi::Stream *stream = nullptr; - int index; - - if (!isRunning()) - return; - - for (RPi::Stream &s : isp_) { - index = s.getBufferId(buffer); - if (index != -1) { - stream = &s; - break; - } - } - - /* The buffer must belong to one of our ISP output streams. */ - ASSERT(stream); - - LOG(RPI, Debug) << "Stream " << stream->name() << " buffer complete" - << ", buffer id " << index - << ", timestamp: " << buffer->metadata().timestamp; - - /* - * ISP statistics buffer must not be re-queued or sent back to the - * application until after the IPA signals so. - */ - if (stream == &isp_[Isp::Stats]) { - ipa_->signalStatReady(RPi::MaskStats | static_cast(index), - requestQueue_.front()->sequence()); - } else { - /* Any other ISP output can be handed back to the application now. */ - handleStreamBuffer(buffer, stream); - } - - /* - * Increment the number of ISP outputs generated. - * This is needed to track dropped frames. - */ - ispOutputCount_++; - - handleState(); -} - -void RPiCameraData::clearIncompleteRequests() -{ - /* - * All outstanding requests (and associated buffers) must be returned - * back to the application. - */ - while (!requestQueue_.empty()) { - Request *request = requestQueue_.front(); - - for (auto &b : request->buffers()) { - FrameBuffer *buffer = b.second; - /* - * Has the buffer already been handed back to the - * request? If not, do so now. - */ - if (buffer->request()) { - buffer->_d()->cancel(); - pipe()->completeBuffer(request, buffer); - } - } - - pipe()->completeRequest(request); - requestQueue_.pop_front(); - } -} - -void RPiCameraData::handleStreamBuffer(FrameBuffer *buffer, RPi::Stream *stream) -{ - /* - * It is possible to be here without a pending request, so check - * that we actually have one to action, otherwise we just return - * buffer back to the stream. - */ - Request *request = requestQueue_.empty() ? nullptr : requestQueue_.front(); - if (!dropFrameCount_ && request && request->findBuffer(stream) == buffer) { - /* - * Check if this is an externally provided buffer, and if - * so, we must stop tracking it in the pipeline handler. - */ - handleExternalBuffer(buffer, stream); - /* - * Tag the buffer as completed, returning it to the - * application. - */ - pipe()->completeBuffer(request, buffer); - } else { - /* - * This buffer was not part of the Request (which happens if an - * internal buffer was used for an external stream, or - * unconditionally for internal streams), or there is no pending - * request, so we can recycle it. - */ - stream->returnBuffer(buffer); - } -} - -void RPiCameraData::handleExternalBuffer(FrameBuffer *buffer, RPi::Stream *stream) -{ - unsigned int id = stream->getBufferId(buffer); - - if (!(id & RPi::MaskExternalBuffer)) - return; - - /* Stop the Stream object from tracking the buffer. */ - stream->removeExternalBuffer(buffer); -} - -void RPiCameraData::handleState() -{ - switch (state_) { - case State::Stopped: - case State::Busy: - case State::Error: - break; - - case State::IpaComplete: - /* If the request is completed, we will switch to Idle state. */ - checkRequestCompleted(); - /* - * No break here, we want to try running the pipeline again. - * The fallthrough clause below suppresses compiler warnings. - */ - [[fallthrough]]; - - case State::Idle: - tryRunPipeline(); - break; - } -} - -void RPiCameraData::checkRequestCompleted() -{ - bool requestCompleted = false; - /* - * If we are dropping this frame, do not touch the request, simply - * change the state to IDLE when ready. - */ - if (!dropFrameCount_) { - Request *request = requestQueue_.front(); - if (request->hasPendingBuffers()) - return; - - /* Must wait for metadata to be filled in before completing. */ - if (state_ != State::IpaComplete) - return; - - pipe()->completeRequest(request); - requestQueue_.pop_front(); - requestCompleted = true; - } - - /* - * Make sure we have three outputs completed in the case of a dropped - * frame. - */ - if (state_ == State::IpaComplete && - ((ispOutputCount_ == 3 && dropFrameCount_) || requestCompleted)) { - state_ = State::Idle; - if (dropFrameCount_) { - dropFrameCount_--; - LOG(RPI, Debug) << "Dropping frame at the request of the IPA (" - << dropFrameCount_ << " left)"; - } - } -} - -Rectangle RPiCameraData::scaleIspCrop(const Rectangle &ispCrop) const -{ - /* - * Scale a crop rectangle defined in the ISP's coordinates into native sensor - * coordinates. - */ - Rectangle nativeCrop = ispCrop.scaledBy(sensorInfo_.analogCrop.size(), - sensorInfo_.outputSize); - nativeCrop.translateBy(sensorInfo_.analogCrop.topLeft()); - return nativeCrop; -} - -void RPiCameraData::applyScalerCrop(const ControlList &controls) -{ - const auto &scalerCrop = controls.get(controls::ScalerCrop); - if (scalerCrop) { - Rectangle nativeCrop = *scalerCrop; - - if (!nativeCrop.width || !nativeCrop.height) - nativeCrop = { 0, 0, 1, 1 }; - - /* Create a version of the crop scaled to ISP (camera mode) pixels. */ - Rectangle ispCrop = nativeCrop.translatedBy(-sensorInfo_.analogCrop.topLeft()); - ispCrop.scaleBy(sensorInfo_.outputSize, sensorInfo_.analogCrop.size()); - - /* - * The crop that we set must be: - * 1. At least as big as ispMinCropSize_, once that's been - * enlarged to the same aspect ratio. - * 2. With the same mid-point, if possible. - * 3. But it can't go outside the sensor area. - */ - Size minSize = ispMinCropSize_.expandedToAspectRatio(nativeCrop.size()); - Size size = ispCrop.size().expandedTo(minSize); - ispCrop = size.centeredTo(ispCrop.center()).enclosedIn(Rectangle(sensorInfo_.outputSize)); - - if (ispCrop != ispCrop_) { - isp_[Isp::Input].dev()->setSelection(V4L2_SEL_TGT_CROP, &ispCrop); - ispCrop_ = ispCrop; - - /* - * Also update the ScalerCrop in the metadata with what we actually - * used. But we must first rescale that from ISP (camera mode) pixels - * back into sensor native pixels. - */ - scalerCrop_ = scaleIspCrop(ispCrop_); - } - } -} - -void RPiCameraData::fillRequestMetadata(const ControlList &bufferControls, - Request *request) -{ - request->metadata().set(controls::SensorTimestamp, - bufferControls.get(controls::SensorTimestamp).value_or(0)); - - request->metadata().set(controls::ScalerCrop, scalerCrop_); -} - -void RPiCameraData::tryRunPipeline() -{ - FrameBuffer *embeddedBuffer; - BayerFrame bayerFrame; - - /* If any of our request or buffer queues are empty, we cannot proceed. */ - if (state_ != State::Idle || requestQueue_.empty() || - bayerQueue_.empty() || (embeddedQueue_.empty() && sensorMetadata_)) - return; - - if (!findMatchingBuffers(bayerFrame, embeddedBuffer)) - return; - - /* Take the first request from the queue and action the IPA. */ - Request *request = requestQueue_.front(); - - /* See if a new ScalerCrop value needs to be applied. */ - applyScalerCrop(request->controls()); - - /* - * Clear the request metadata and fill it with some initial non-IPA - * related controls. We clear it first because the request metadata - * may have been populated if we have dropped the previous frame. - */ - request->metadata().clear(); - fillRequestMetadata(bayerFrame.controls, request); - - /* - * Process all the user controls by the IPA. Once this is complete, we - * queue the ISP output buffer listed in the request to start the HW - * pipeline. - */ - ipa_->signalQueueRequest(request->controls()); - - /* Set our state to say the pipeline is active. */ - state_ = State::Busy; - - unsigned int bayerId = unicam_[Unicam::Image].getBufferId(bayerFrame.buffer); - - LOG(RPI, Debug) << "Signalling signalIspPrepare:" - << " Bayer buffer id: " << bayerId; - - ipa::RPi::ISPConfig ispPrepare; - ispPrepare.bayerBufferId = RPi::MaskBayerData | bayerId; - ispPrepare.controls = std::move(bayerFrame.controls); - ispPrepare.ipaContext = request->sequence(); - ispPrepare.delayContext = bayerFrame.delayContext; - - if (embeddedBuffer) { - unsigned int embeddedId = unicam_[Unicam::Embedded].getBufferId(embeddedBuffer); - - ispPrepare.embeddedBufferId = RPi::MaskEmbeddedData | embeddedId; - ispPrepare.embeddedBufferPresent = true; - - LOG(RPI, Debug) << "Signalling signalIspPrepare:" - << " Embedded buffer id: " << embeddedId; - } - - ipa_->signalIspPrepare(ispPrepare); -} - -bool RPiCameraData::findMatchingBuffers(BayerFrame &bayerFrame, FrameBuffer *&embeddedBuffer) -{ - if (bayerQueue_.empty()) - return false; - - /* - * Find the embedded data buffer with a matching timestamp to pass to - * the IPA. Any embedded buffers with a timestamp lower than the - * current bayer buffer will be removed and re-queued to the driver. - */ - uint64_t ts = bayerQueue_.front().buffer->metadata().timestamp; - embeddedBuffer = nullptr; - while (!embeddedQueue_.empty()) { - FrameBuffer *b = embeddedQueue_.front(); - if (b->metadata().timestamp < ts) { - embeddedQueue_.pop(); - unicam_[Unicam::Embedded].returnBuffer(b); - LOG(RPI, Debug) << "Dropping unmatched input frame in stream " - << unicam_[Unicam::Embedded].name(); - } else if (b->metadata().timestamp == ts) { - /* Found a match! */ - embeddedBuffer = b; - embeddedQueue_.pop(); - break; - } else { - break; /* Only higher timestamps from here. */ - } - } - - if (!embeddedBuffer && sensorMetadata_) { - if (embeddedQueue_.empty()) { - /* - * If the embedded buffer queue is empty, wait for the next - * buffer to arrive - dequeue ordering may send the image - * buffer first. - */ - LOG(RPI, Debug) << "Waiting for next embedded buffer."; - return false; - } - - /* Log if there is no matching embedded data buffer found. */ - LOG(RPI, Debug) << "Returning bayer frame without a matching embedded buffer."; - } - - bayerFrame = std::move(bayerQueue_.front()); - bayerQueue_.pop(); - - return true; -} - -REGISTER_PIPELINE_HANDLER(PipelineHandlerRPi) - -} /* namespace libcamera */ diff --git a/src/libcamera/pipeline/raspberrypi/rpi_stream.cpp b/src/libcamera/pipeline/raspberrypi/rpi_stream.cpp deleted file mode 100644 index 2bb10f25..00000000 --- a/src/libcamera/pipeline/raspberrypi/rpi_stream.cpp +++ /dev/null @@ -1,250 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -/* - * Copyright (C) 2020, Raspberry Pi Ltd - * - * rpi_stream.cpp - Raspberry Pi device stream abstraction class. - */ -#include "rpi_stream.h" - -#include - -namespace libcamera { - -LOG_DEFINE_CATEGORY(RPISTREAM) - -namespace RPi { - -V4L2VideoDevice *Stream::dev() const -{ - return dev_.get(); -} - -std::string Stream::name() const -{ - return name_; -} - -void Stream::resetBuffers() -{ - /* Add all internal buffers to the queue of usable buffers. */ - availableBuffers_ = {}; - for (auto const &buffer : internalBuffers_) - availableBuffers_.push(buffer.get()); -} - -void Stream::setExternal(bool external) -{ - /* Import streams cannot be external. */ - ASSERT(!external || !importOnly_); - external_ = external; -} - -bool Stream::isExternal() const -{ - return external_; -} - -void Stream::setExportedBuffers(std::vector> *buffers) -{ - for (auto const &buffer : *buffers) - bufferMap_.emplace(id_.get(), buffer.get()); -} - -const BufferMap &Stream::getBuffers() const -{ - return bufferMap_; -} - -int Stream::getBufferId(FrameBuffer *buffer) const -{ - if (importOnly_) - return -1; - - /* Find the buffer in the map, and return the buffer id. */ - auto it = std::find_if(bufferMap_.begin(), bufferMap_.end(), - [&buffer](auto const &p) { return p.second == buffer; }); - - if (it == bufferMap_.end()) - return -1; - - return it->first; -} - -void Stream::setExternalBuffer(FrameBuffer *buffer) -{ - bufferMap_.emplace(BufferMask::MaskExternalBuffer | id_.get(), buffer); -} - -void Stream::removeExternalBuffer(FrameBuffer *buffer) -{ - int id = getBufferId(buffer); - - /* Ensure we have this buffer in the stream, and it is marked external. */ - ASSERT(id != -1 && (id & BufferMask::MaskExternalBuffer)); - bufferMap_.erase(id); -} - -int Stream::prepareBuffers(unsigned int count) -{ - int ret; - - if (!importOnly_) { - if (count) { - /* Export some frame buffers for internal use. */ - ret = dev_->exportBuffers(count, &internalBuffers_); - if (ret < 0) - return ret; - - /* Add these exported buffers to the internal/external buffer list. */ - setExportedBuffers(&internalBuffers_); - resetBuffers(); - } - - /* We must import all internal/external exported buffers. */ - count = bufferMap_.size(); - } - - /* - * If this is an external stream, we must allocate slots for buffers that - * might be externally allocated. We have no indication of how many buffers - * may be used, so this might overallocate slots in the buffer cache. - * Similarly, if this stream is only importing buffers, we do the same. - * - * \todo Find a better heuristic, or, even better, an exact solution to - * this issue. - */ - if (isExternal() || importOnly_) - count = count * 2; - - return dev_->importBuffers(count); -} - -int Stream::queueBuffer(FrameBuffer *buffer) -{ - /* - * A nullptr buffer implies an external stream, but no external - * buffer has been supplied in the Request. So, pick one from the - * availableBuffers_ queue. - */ - if (!buffer) { - if (availableBuffers_.empty()) { - LOG(RPISTREAM, Debug) << "No buffers available for " - << name_; - /* - * Note that we need to queue an internal buffer as soon - * as one becomes available. - */ - requestBuffers_.push(nullptr); - return 0; - } - - buffer = availableBuffers_.front(); - availableBuffers_.pop(); - } - - /* - * If no earlier requests are pending to be queued we can go ahead and - * queue this buffer into the device. - */ - if (requestBuffers_.empty()) - return queueToDevice(buffer); - - /* - * There are earlier Request buffers to be queued, so this buffer must go - * on the waiting list. - */ - requestBuffers_.push(buffer); - - return 0; -} - -void Stream::returnBuffer(FrameBuffer *buffer) -{ - if (!external_) { - /* For internal buffers, simply requeue back to the device. */ - queueToDevice(buffer); - return; - } - - /* Push this buffer back into the queue to be used again. */ - availableBuffers_.push(buffer); - - /* Allow the buffer id to be reused. */ - id_.release(getBufferId(buffer)); - - /* - * Do we have any Request buffers that are waiting to be queued? - * If so, do it now as availableBuffers_ will not be empty. - */ - while (!requestBuffers_.empty()) { - FrameBuffer *requestBuffer = requestBuffers_.front(); - - if (!requestBuffer) { - /* - * We want to queue an internal buffer, but none - * are available. Can't do anything, quit the loop. - */ - if (availableBuffers_.empty()) - break; - - /* - * We want to queue an internal buffer, and at least one - * is available. - */ - requestBuffer = availableBuffers_.front(); - availableBuffers_.pop(); - } - - requestBuffers_.pop(); - queueToDevice(requestBuffer); - } -} - -int Stream::queueAllBuffers() -{ - int ret; - - if (external_) - return 0; - - while (!availableBuffers_.empty()) { - ret = queueBuffer(availableBuffers_.front()); - if (ret < 0) - return ret; - - availableBuffers_.pop(); - } - - return 0; -} - -void Stream::releaseBuffers() -{ - dev_->releaseBuffers(); - clearBuffers(); -} - -void Stream::clearBuffers() -{ - availableBuffers_ = std::queue{}; - requestBuffers_ = std::queue{}; - internalBuffers_.clear(); - bufferMap_.clear(); - id_.reset(); -} - -int Stream::queueToDevice(FrameBuffer *buffer) -{ - LOG(RPISTREAM, Debug) << "Queuing buffer " << getBufferId(buffer) - << " for " << name_; - - int ret = dev_->queueBuffer(buffer); - if (ret) - LOG(RPISTREAM, Error) << "Failed to queue buffer for " - << name_; - return ret; -} - -} /* namespace RPi */ - -} /* namespace libcamera */ diff --git a/src/libcamera/pipeline/raspberrypi/rpi_stream.h b/src/libcamera/pipeline/raspberrypi/rpi_stream.h deleted file mode 100644 index b8bd79cf..00000000 --- a/src/libcamera/pipeline/raspberrypi/rpi_stream.h +++ /dev/null @@ -1,185 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -/* - * Copyright (C) 2020, Raspberry Pi Ltd - * - * rpi_stream.h - Raspberry Pi device stream abstraction class. - */ - -#pragma once - -#include -#include -#include -#include - -#include - -#include "libcamera/internal/v4l2_videodevice.h" - -namespace libcamera { - -namespace RPi { - -using BufferMap = std::unordered_map; - -enum BufferMask { - MaskID = 0x00ffff, - MaskStats = 0x010000, - MaskEmbeddedData = 0x020000, - MaskBayerData = 0x040000, - MaskExternalBuffer = 0x100000, -}; - -/* - * Device stream abstraction for either an internal or external stream. - * Used for both Unicam and the ISP. - */ -class Stream : public libcamera::Stream -{ -public: - Stream() - : id_(BufferMask::MaskID) - { - } - - Stream(const char *name, MediaEntity *dev, bool importOnly = false) - : external_(false), importOnly_(importOnly), name_(name), - dev_(std::make_unique(dev)), id_(BufferMask::MaskID) - { - } - - V4L2VideoDevice *dev() const; - std::string name() const; - bool isImporter() const; - void resetBuffers(); - - void setExternal(bool external); - bool isExternal() const; - - void setExportedBuffers(std::vector> *buffers); - const BufferMap &getBuffers() const; - int getBufferId(FrameBuffer *buffer) const; - - void setExternalBuffer(FrameBuffer *buffer); - void removeExternalBuffer(FrameBuffer *buffer); - - int prepareBuffers(unsigned int count); - int queueBuffer(FrameBuffer *buffer); - void returnBuffer(FrameBuffer *buffer); - - int queueAllBuffers(); - void releaseBuffers(); - -private: - class IdGenerator - { - public: - IdGenerator(int max) - : max_(max), id_(0) - { - } - - int get() - { - int id; - if (!recycle_.empty()) { - id = recycle_.front(); - recycle_.pop(); - } else { - id = id_++; - ASSERT(id_ <= max_); - } - return id; - } - - void release(int id) - { - recycle_.push(id); - } - - void reset() - { - id_ = 0; - recycle_ = {}; - } - - private: - int max_; - int id_; - std::queue recycle_; - }; - - void clearBuffers(); - int queueToDevice(FrameBuffer *buffer); - - /* - * Indicates that this stream is active externally, i.e. the buffers - * might be provided by (and returned to) the application. - */ - bool external_; - - /* Indicates that this stream only imports buffers, e.g. ISP input. */ - bool importOnly_; - - /* Stream name identifier. */ - std::string name_; - - /* The actual device stream. */ - std::unique_ptr dev_; - - /* Tracks a unique id key for the bufferMap_ */ - IdGenerator id_; - - /* All frame buffers associated with this device stream. */ - BufferMap bufferMap_; - - /* - * List of frame buffers that we can use if none have been provided by - * the application for external streams. This is populated by the - * buffers exported internally. - */ - std::queue availableBuffers_; - - /* - * List of frame buffers that are to be queued into the device from a Request. - * A nullptr indicates any internal buffer can be used (from availableBuffers_), - * whereas a valid pointer indicates an external buffer to be queued. - * - * Ordering buffers to be queued is important here as it must match the - * requests coming from the application. - */ - std::queue requestBuffers_; - - /* - * This is a list of buffers exported internally. Need to keep this around - * as the stream needs to maintain ownership of these buffers. - */ - std::vector> internalBuffers_; -}; - -/* - * The following class is just a convenient (and typesafe) array of device - * streams indexed with an enum class. - */ -template -class Device : public std::array -{ -private: - constexpr auto index(E e) const noexcept - { - return static_cast>(e); - } -public: - Stream &operator[](E e) - { - return std::array::operator[](index(e)); - } - const Stream &operator[](E e) const - { - return std::array::operator[](index(e)); - } -}; - -} /* namespace RPi */ - -} /* namespace libcamera */ diff --git a/src/libcamera/pipeline/rpi/common/delayed_controls.cpp b/src/libcamera/pipeline/rpi/common/delayed_controls.cpp new file mode 100644 index 00000000..3db92e7d --- /dev/null +++ b/src/libcamera/pipeline/rpi/common/delayed_controls.cpp @@ -0,0 +1,293 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2020, Raspberry Pi Ltd + * + * delayed_controls.cpp - Helper to deal with controls that take effect with a delay + * + * Note: This has been forked from the libcamera core implementation. + */ + +#include "delayed_controls.h" + +#include + +#include + +#include "libcamera/internal/v4l2_device.h" + +/** + * \file delayed_controls.h + * \brief Helper to deal with controls that take effect with a delay + */ + +namespace libcamera { + +LOG_DEFINE_CATEGORY(RPiDelayedControls) + +namespace RPi { + +/** + * \class DelayedControls + * \brief Helper to deal with controls that take effect with a delay + * + * Some sensor controls take effect with a delay as the sensor needs time to + * adjust, for example exposure and analog gain. This is a helper class to deal + * with such controls and the intended users are pipeline handlers. + * + * The idea is to extend the concept of the buffer depth of a pipeline the + * application needs to maintain to also cover controls. Just as with buffer + * depth if the application keeps the number of requests queued above the + * control depth the controls are guaranteed to take effect for the correct + * request. The control depth is determined by the control with the greatest + * delay. + */ + +/** + * \struct DelayedControls::ControlParams + * \brief Parameters associated with controls handled by the \a DelayedControls + * helper class + * + * \var ControlParams::delay + * \brief Frame delay from setting the control on a sensor device to when it is + * consumed during framing. + * + * \var ControlParams::priorityWrite + * \brief Flag to indicate that this control must be applied ahead of, and + * separately from the other controls. + * + * Typically set for the \a V4L2_CID_VBLANK control so that the device driver + * does not reject \a V4L2_CID_EXPOSURE control values that may be outside of + * the existing vertical blanking specified bounds, but are within the new + * blanking bounds. + */ + +/** + * \brief Construct a DelayedControls instance + * \param[in] device The V4L2 device the controls have to be applied to + * \param[in] controlParams Map of the numerical V4L2 control ids to their + * associated control parameters. + * + * The control parameters comprise of delays (in frames) and a priority write + * flag. If this flag is set, the relevant control is written separately from, + * and ahead of the rest of the batched controls. + * + * Only controls specified in \a controlParams are handled. If it's desired to + * mix delayed controls and controls that take effect immediately the immediate + * controls must be listed in the \a controlParams map with a delay value of 0. + */ +DelayedControls::DelayedControls(V4L2Device *device, + const std::unordered_map &controlParams) + : device_(device), maxDelay_(0) +{ + const ControlInfoMap &controls = device_->controls(); + + /* + * Create a map of control ids to delays for controls exposed by the + * device. + */ + for (auto const ¶m : controlParams) { + auto it = controls.find(param.first); + if (it == controls.end()) { + LOG(RPiDelayedControls, Error) + << "Delay request for control id " + << utils::hex(param.first) + << " but control is not exposed by device " + << device_->deviceNode(); + continue; + } + + const ControlId *id = it->first; + + controlParams_[id] = param.second; + + LOG(RPiDelayedControls, Debug) + << "Set a delay of " << controlParams_[id].delay + << " and priority write flag " << controlParams_[id].priorityWrite + << " for " << id->name(); + + maxDelay_ = std::max(maxDelay_, controlParams_[id].delay); + } + + reset(0); +} + +/** + * \brief Reset state machine + * + * Resets the state machine to a starting position based on control values + * retrieved from the device. + */ +void DelayedControls::reset(unsigned int cookie) +{ + queueCount_ = 1; + writeCount_ = 0; + cookies_[0] = cookie; + + /* Retrieve control as reported by the device. */ + std::vector ids; + for (auto const ¶m : controlParams_) + ids.push_back(param.first->id()); + + ControlList controls = device_->getControls(ids); + + /* Seed the control queue with the controls reported by the device. */ + values_.clear(); + for (const auto &ctrl : controls) { + const ControlId *id = device_->controls().idmap().at(ctrl.first); + /* + * Do not mark this control value as updated, it does not need + * to be written to to device on startup. + */ + values_[id][0] = Info(ctrl.second, false); + } +} + +/** + * \brief Push a set of controls on the queue + * \param[in] controls List of controls to add to the device queue + * + * Push a set of controls to the control queue. This increases the control queue + * depth by one. + * + * \returns true if \a controls are accepted, or false otherwise + */ +bool DelayedControls::push(const ControlList &controls, const unsigned int cookie) +{ + /* Copy state from previous frame. */ + for (auto &ctrl : values_) { + Info &info = ctrl.second[queueCount_]; + info = values_[ctrl.first][queueCount_ - 1]; + info.updated = false; + } + + /* Update with new controls. */ + const ControlIdMap &idmap = device_->controls().idmap(); + for (const auto &control : controls) { + const auto &it = idmap.find(control.first); + if (it == idmap.end()) { + LOG(RPiDelayedControls, Warning) + << "Unknown control " << control.first; + return false; + } + + const ControlId *id = it->second; + + if (controlParams_.find(id) == controlParams_.end()) + return false; + + Info &info = values_[id][queueCount_]; + + info = Info(control.second); + + LOG(RPiDelayedControls, Debug) + << "Queuing " << id->name() + << " to " << info.toString() + << " at index " << queueCount_; + } + + cookies_[queueCount_] = cookie; + queueCount_++; + + return true; +} + +/** + * \brief Read back controls in effect at a sequence number + * \param[in] sequence The sequence number to get controls for + * + * Read back what controls where in effect at a specific sequence number. The + * history is a ring buffer of 16 entries where new and old values coexist. It's + * the callers responsibility to not read too old sequence numbers that have been + * pushed out of the history. + * + * Historic values are evicted by pushing new values onto the queue using + * push(). The max history from the current sequence number that yields valid + * values are thus 16 minus number of controls pushed. + * + * \return The controls at \a sequence number + */ +std::pair DelayedControls::get(uint32_t sequence) +{ + unsigned int index = std::max(0, sequence - maxDelay_); + + ControlList out(device_->controls()); + for (const auto &ctrl : values_) { + const ControlId *id = ctrl.first; + const Info &info = ctrl.second[index]; + + out.set(id->id(), info); + + LOG(RPiDelayedControls, Debug) + << "Reading " << id->name() + << " to " << info.toString() + << " at index " << index; + } + + return { out, cookies_[index] }; +} + +/** + * \brief Inform DelayedControls of the start of a new frame + * \param[in] sequence Sequence number of the frame that started + * + * Inform the state machine that a new frame has started and of its sequence + * number. Any user of these helpers is responsible to inform the helper about + * the start of any frame. This can be connected with ease to the start of a + * exposure (SOE) V4L2 event. + */ +void DelayedControls::applyControls(uint32_t sequence) +{ + LOG(RPiDelayedControls, Debug) << "frame " << sequence << " started"; + + /* + * Create control list peeking ahead in the value queue to ensure + * values are set in time to satisfy the sensor delay. + */ + ControlList out(device_->controls()); + for (auto &ctrl : values_) { + const ControlId *id = ctrl.first; + unsigned int delayDiff = maxDelay_ - controlParams_[id].delay; + unsigned int index = std::max(0, writeCount_ - delayDiff); + Info &info = ctrl.second[index]; + + if (info.updated) { + if (controlParams_[id].priorityWrite) { + /* + * This control must be written now, it could + * affect validity of the other controls. + */ + ControlList priority(device_->controls()); + priority.set(id->id(), info); + device_->setControls(&priority); + } else { + /* + * Batch up the list of controls and write them + * at the end of the function. + */ + out.set(id->id(), info); + } + + LOG(RPiDelayedControls, Debug) + << "Setting " << id->name() + << " to " << info.toString() + << " at index " << index; + + /* Done with this update, so mark as completed. */ + info.updated = false; + } + } + + writeCount_ = sequence + 1; + + while (writeCount_ > queueCount_) { + LOG(RPiDelayedControls, Debug) + << "Queue is empty, auto queue no-op."; + push({}, cookies_[queueCount_ - 1]); + } + + device_->setControls(&out); +} + +} /* namespace RPi */ + +} /* namespace libcamera */ diff --git a/src/libcamera/pipeline/rpi/common/delayed_controls.h b/src/libcamera/pipeline/rpi/common/delayed_controls.h new file mode 100644 index 00000000..61f755f0 --- /dev/null +++ b/src/libcamera/pipeline/rpi/common/delayed_controls.h @@ -0,0 +1,87 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2020, Raspberry Pi Ltd + * + * delayed_controls.h - Helper to deal with controls that take effect with a delay + * + * Note: This has been forked from the libcamera core implementation. + */ + +#pragma once + +#include +#include +#include + +#include + +namespace libcamera { + +class V4L2Device; + +namespace RPi { + +class DelayedControls +{ +public: + struct ControlParams { + unsigned int delay; + bool priorityWrite; + }; + + DelayedControls(V4L2Device *device, + const std::unordered_map &controlParams); + + void reset(unsigned int cookie); + + bool push(const ControlList &controls, unsigned int cookie); + std::pair get(uint32_t sequence); + + void applyControls(uint32_t sequence); + +private: + class Info : public ControlValue + { + public: + Info() + : updated(false) + { + } + + Info(const ControlValue &v, bool updated_ = true) + : ControlValue(v), updated(updated_) + { + } + + bool updated; + }; + + static constexpr int listSize = 16; + template + class RingBuffer : public std::array + { + public: + T &operator[](unsigned int index) + { + return std::array::operator[](index % listSize); + } + + const T &operator[](unsigned int index) const + { + return std::array::operator[](index % listSize); + } + }; + + V4L2Device *device_; + std::unordered_map controlParams_; + unsigned int maxDelay_; + + uint32_t queueCount_; + uint32_t writeCount_; + std::unordered_map> values_; + RingBuffer cookies_; +}; + +} /* namespace RPi */ + +} /* namespace libcamera */ diff --git a/src/libcamera/pipeline/rpi/common/meson.build b/src/libcamera/pipeline/rpi/common/meson.build new file mode 100644 index 00000000..2ad594cf --- /dev/null +++ b/src/libcamera/pipeline/rpi/common/meson.build @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: CC0-1.0 + +libcamera_sources += files([ + 'delayed_controls.cpp', + 'rpi_stream.cpp', +]) diff --git a/src/libcamera/pipeline/rpi/common/rpi_stream.cpp b/src/libcamera/pipeline/rpi/common/rpi_stream.cpp new file mode 100644 index 00000000..2bb10f25 --- /dev/null +++ b/src/libcamera/pipeline/rpi/common/rpi_stream.cpp @@ -0,0 +1,250 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2020, Raspberry Pi Ltd + * + * rpi_stream.cpp - Raspberry Pi device stream abstraction class. + */ +#include "rpi_stream.h" + +#include + +namespace libcamera { + +LOG_DEFINE_CATEGORY(RPISTREAM) + +namespace RPi { + +V4L2VideoDevice *Stream::dev() const +{ + return dev_.get(); +} + +std::string Stream::name() const +{ + return name_; +} + +void Stream::resetBuffers() +{ + /* Add all internal buffers to the queue of usable buffers. */ + availableBuffers_ = {}; + for (auto const &buffer : internalBuffers_) + availableBuffers_.push(buffer.get()); +} + +void Stream::setExternal(bool external) +{ + /* Import streams cannot be external. */ + ASSERT(!external || !importOnly_); + external_ = external; +} + +bool Stream::isExternal() const +{ + return external_; +} + +void Stream::setExportedBuffers(std::vector> *buffers) +{ + for (auto const &buffer : *buffers) + bufferMap_.emplace(id_.get(), buffer.get()); +} + +const BufferMap &Stream::getBuffers() const +{ + return bufferMap_; +} + +int Stream::getBufferId(FrameBuffer *buffer) const +{ + if (importOnly_) + return -1; + + /* Find the buffer in the map, and return the buffer id. */ + auto it = std::find_if(bufferMap_.begin(), bufferMap_.end(), + [&buffer](auto const &p) { return p.second == buffer; }); + + if (it == bufferMap_.end()) + return -1; + + return it->first; +} + +void Stream::setExternalBuffer(FrameBuffer *buffer) +{ + bufferMap_.emplace(BufferMask::MaskExternalBuffer | id_.get(), buffer); +} + +void Stream::removeExternalBuffer(FrameBuffer *buffer) +{ + int id = getBufferId(buffer); + + /* Ensure we have this buffer in the stream, and it is marked external. */ + ASSERT(id != -1 && (id & BufferMask::MaskExternalBuffer)); + bufferMap_.erase(id); +} + +int Stream::prepareBuffers(unsigned int count) +{ + int ret; + + if (!importOnly_) { + if (count) { + /* Export some frame buffers for internal use. */ + ret = dev_->exportBuffers(count, &internalBuffers_); + if (ret < 0) + return ret; + + /* Add these exported buffers to the internal/external buffer list. */ + setExportedBuffers(&internalBuffers_); + resetBuffers(); + } + + /* We must import all internal/external exported buffers. */ + count = bufferMap_.size(); + } + + /* + * If this is an external stream, we must allocate slots for buffers that + * might be externally allocated. We have no indication of how many buffers + * may be used, so this might overallocate slots in the buffer cache. + * Similarly, if this stream is only importing buffers, we do the same. + * + * \todo Find a better heuristic, or, even better, an exact solution to + * this issue. + */ + if (isExternal() || importOnly_) + count = count * 2; + + return dev_->importBuffers(count); +} + +int Stream::queueBuffer(FrameBuffer *buffer) +{ + /* + * A nullptr buffer implies an external stream, but no external + * buffer has been supplied in the Request. So, pick one from the + * availableBuffers_ queue. + */ + if (!buffer) { + if (availableBuffers_.empty()) { + LOG(RPISTREAM, Debug) << "No buffers available for " + << name_; + /* + * Note that we need to queue an internal buffer as soon + * as one becomes available. + */ + requestBuffers_.push(nullptr); + return 0; + } + + buffer = availableBuffers_.front(); + availableBuffers_.pop(); + } + + /* + * If no earlier requests are pending to be queued we can go ahead and + * queue this buffer into the device. + */ + if (requestBuffers_.empty()) + return queueToDevice(buffer); + + /* + * There are earlier Request buffers to be queued, so this buffer must go + * on the waiting list. + */ + requestBuffers_.push(buffer); + + return 0; +} + +void Stream::returnBuffer(FrameBuffer *buffer) +{ + if (!external_) { + /* For internal buffers, simply requeue back to the device. */ + queueToDevice(buffer); + return; + } + + /* Push this buffer back into the queue to be used again. */ + availableBuffers_.push(buffer); + + /* Allow the buffer id to be reused. */ + id_.release(getBufferId(buffer)); + + /* + * Do we have any Request buffers that are waiting to be queued? + * If so, do it now as availableBuffers_ will not be empty. + */ + while (!requestBuffers_.empty()) { + FrameBuffer *requestBuffer = requestBuffers_.front(); + + if (!requestBuffer) { + /* + * We want to queue an internal buffer, but none + * are available. Can't do anything, quit the loop. + */ + if (availableBuffers_.empty()) + break; + + /* + * We want to queue an internal buffer, and at least one + * is available. + */ + requestBuffer = availableBuffers_.front(); + availableBuffers_.pop(); + } + + requestBuffers_.pop(); + queueToDevice(requestBuffer); + } +} + +int Stream::queueAllBuffers() +{ + int ret; + + if (external_) + return 0; + + while (!availableBuffers_.empty()) { + ret = queueBuffer(availableBuffers_.front()); + if (ret < 0) + return ret; + + availableBuffers_.pop(); + } + + return 0; +} + +void Stream::releaseBuffers() +{ + dev_->releaseBuffers(); + clearBuffers(); +} + +void Stream::clearBuffers() +{ + availableBuffers_ = std::queue{}; + requestBuffers_ = std::queue{}; + internalBuffers_.clear(); + bufferMap_.clear(); + id_.reset(); +} + +int Stream::queueToDevice(FrameBuffer *buffer) +{ + LOG(RPISTREAM, Debug) << "Queuing buffer " << getBufferId(buffer) + << " for " << name_; + + int ret = dev_->queueBuffer(buffer); + if (ret) + LOG(RPISTREAM, Error) << "Failed to queue buffer for " + << name_; + return ret; +} + +} /* namespace RPi */ + +} /* namespace libcamera */ diff --git a/src/libcamera/pipeline/rpi/common/rpi_stream.h b/src/libcamera/pipeline/rpi/common/rpi_stream.h new file mode 100644 index 00000000..b8bd79cf --- /dev/null +++ b/src/libcamera/pipeline/rpi/common/rpi_stream.h @@ -0,0 +1,185 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2020, Raspberry Pi Ltd + * + * rpi_stream.h - Raspberry Pi device stream abstraction class. + */ + +#pragma once + +#include +#include +#include +#include + +#include + +#include "libcamera/internal/v4l2_videodevice.h" + +namespace libcamera { + +namespace RPi { + +using BufferMap = std::unordered_map; + +enum BufferMask { + MaskID = 0x00ffff, + MaskStats = 0x010000, + MaskEmbeddedData = 0x020000, + MaskBayerData = 0x040000, + MaskExternalBuffer = 0x100000, +}; + +/* + * Device stream abstraction for either an internal or external stream. + * Used for both Unicam and the ISP. + */ +class Stream : public libcamera::Stream +{ +public: + Stream() + : id_(BufferMask::MaskID) + { + } + + Stream(const char *name, MediaEntity *dev, bool importOnly = false) + : external_(false), importOnly_(importOnly), name_(name), + dev_(std::make_unique(dev)), id_(BufferMask::MaskID) + { + } + + V4L2VideoDevice *dev() const; + std::string name() const; + bool isImporter() const; + void resetBuffers(); + + void setExternal(bool external); + bool isExternal() const; + + void setExportedBuffers(std::vector> *buffers); + const BufferMap &getBuffers() const; + int getBufferId(FrameBuffer *buffer) const; + + void setExternalBuffer(FrameBuffer *buffer); + void removeExternalBuffer(FrameBuffer *buffer); + + int prepareBuffers(unsigned int count); + int queueBuffer(FrameBuffer *buffer); + void returnBuffer(FrameBuffer *buffer); + + int queueAllBuffers(); + void releaseBuffers(); + +private: + class IdGenerator + { + public: + IdGenerator(int max) + : max_(max), id_(0) + { + } + + int get() + { + int id; + if (!recycle_.empty()) { + id = recycle_.front(); + recycle_.pop(); + } else { + id = id_++; + ASSERT(id_ <= max_); + } + return id; + } + + void release(int id) + { + recycle_.push(id); + } + + void reset() + { + id_ = 0; + recycle_ = {}; + } + + private: + int max_; + int id_; + std::queue recycle_; + }; + + void clearBuffers(); + int queueToDevice(FrameBuffer *buffer); + + /* + * Indicates that this stream is active externally, i.e. the buffers + * might be provided by (and returned to) the application. + */ + bool external_; + + /* Indicates that this stream only imports buffers, e.g. ISP input. */ + bool importOnly_; + + /* Stream name identifier. */ + std::string name_; + + /* The actual device stream. */ + std::unique_ptr dev_; + + /* Tracks a unique id key for the bufferMap_ */ + IdGenerator id_; + + /* All frame buffers associated with this device stream. */ + BufferMap bufferMap_; + + /* + * List of frame buffers that we can use if none have been provided by + * the application for external streams. This is populated by the + * buffers exported internally. + */ + std::queue availableBuffers_; + + /* + * List of frame buffers that are to be queued into the device from a Request. + * A nullptr indicates any internal buffer can be used (from availableBuffers_), + * whereas a valid pointer indicates an external buffer to be queued. + * + * Ordering buffers to be queued is important here as it must match the + * requests coming from the application. + */ + std::queue requestBuffers_; + + /* + * This is a list of buffers exported internally. Need to keep this around + * as the stream needs to maintain ownership of these buffers. + */ + std::vector> internalBuffers_; +}; + +/* + * The following class is just a convenient (and typesafe) array of device + * streams indexed with an enum class. + */ +template +class Device : public std::array +{ +private: + constexpr auto index(E e) const noexcept + { + return static_cast>(e); + } +public: + Stream &operator[](E e) + { + return std::array::operator[](index(e)); + } + const Stream &operator[](E e) const + { + return std::array::operator[](index(e)); + } +}; + +} /* namespace RPi */ + +} /* namespace libcamera */ diff --git a/src/libcamera/pipeline/rpi/meson.build b/src/libcamera/pipeline/rpi/meson.build new file mode 100644 index 00000000..2391b6a9 --- /dev/null +++ b/src/libcamera/pipeline/rpi/meson.build @@ -0,0 +1,12 @@ +# SPDX-License-Identifier: CC0-1.0 + +subdir('common') + +foreach pipeline : pipelines + pipeline = pipeline.split('/') + if pipeline.length() < 2 or pipeline[0] != 'rpi' + continue + endif + + subdir(pipeline[1]) +endforeach diff --git a/src/libcamera/pipeline/rpi/vc4/data/example.yaml b/src/libcamera/pipeline/rpi/vc4/data/example.yaml new file mode 100644 index 00000000..c90f518f --- /dev/null +++ b/src/libcamera/pipeline/rpi/vc4/data/example.yaml @@ -0,0 +1,46 @@ +{ + "version": 1.0, + "target": "bcm2835", + + "pipeline_handler": + { + # The minimum number of internal buffers to be allocated for + # Unicam. This value must be greater than 0, but less than or + # equal to min_total_unicam_buffers. + # + # A larger number of internal buffers can reduce the occurrence + # of frame drops during high CPU loads, but might also cause + # additional latency in the system. + # + # Note that the pipeline handler might override this value and + # not allocate any internal buffers if it knows they will never + # be used. For example if the RAW stream is marked as mandatory + # and there are no dropped frames signalled for algorithm + # convergence. + # + # "min_unicam_buffers": 2, + + # The minimum total (internal + external) buffer count used for + # Unicam. The number of internal buffers allocated for Unicam is + # given by: + # + # internal buffer count = max(min_unicam_buffers, + # min_total_unicam_buffers - external buffer count) + # + # "min_total_unicam_buffers": 4, + + # Override any request from the IPA to drop a number of startup + # frames. + # + # "disable_startup_frame_drops": false, + + # Custom timeout value (in ms) for Unicam to use. This overrides + # the value computed by the pipeline handler based on frame + # durations. + # + # Set this value to 0 to use the pipeline handler computed + # timeout value. + # + # "unicam_timeout_value_ms": 0, + } +} diff --git a/src/libcamera/pipeline/rpi/vc4/data/meson.build b/src/libcamera/pipeline/rpi/vc4/data/meson.build new file mode 100644 index 00000000..cca5e388 --- /dev/null +++ b/src/libcamera/pipeline/rpi/vc4/data/meson.build @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: CC0-1.0 + +conf_files = files([ + 'example.yaml', +]) + +install_data(conf_files, + install_dir : pipeline_data_dir / 'rpi' / 'vc4') diff --git a/src/libcamera/pipeline/rpi/vc4/dma_heaps.cpp b/src/libcamera/pipeline/rpi/vc4/dma_heaps.cpp new file mode 100644 index 00000000..317b1fc1 --- /dev/null +++ b/src/libcamera/pipeline/rpi/vc4/dma_heaps.cpp @@ -0,0 +1,90 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2020, Raspberry Pi Ltd + * + * dma_heaps.h - Helper class for dma-heap allocations. + */ + +#include "dma_heaps.h" + +#include +#include +#include +#include +#include +#include + +#include + +/* + * /dev/dma-heap/linux,cma is the dma-heap allocator, which allows dmaheap-cma + * to only have to worry about importing. + * + * Annoyingly, should the cma heap size be specified on the kernel command line + * instead of DT, the heap gets named "reserved" instead. + */ +static constexpr std::array heapNames = { + "/dev/dma_heap/linux,cma", + "/dev/dma_heap/reserved" +}; + +namespace libcamera { + +LOG_DECLARE_CATEGORY(RPI) + +namespace RPi { + +DmaHeap::DmaHeap() +{ + for (const char *name : heapNames) { + int ret = ::open(name, O_RDWR | O_CLOEXEC, 0); + if (ret < 0) { + ret = errno; + LOG(RPI, Debug) << "Failed to open " << name << ": " + << strerror(ret); + continue; + } + + dmaHeapHandle_ = UniqueFD(ret); + break; + } + + if (!dmaHeapHandle_.isValid()) + LOG(RPI, Error) << "Could not open any dmaHeap device"; +} + +DmaHeap::~DmaHeap() = default; + +UniqueFD DmaHeap::alloc(const char *name, std::size_t size) +{ + int ret; + + if (!name) + return {}; + + struct dma_heap_allocation_data alloc = {}; + + alloc.len = size; + alloc.fd_flags = O_CLOEXEC | O_RDWR; + + ret = ::ioctl(dmaHeapHandle_.get(), DMA_HEAP_IOCTL_ALLOC, &alloc); + if (ret < 0) { + LOG(RPI, Error) << "dmaHeap allocation failure for " + << name; + return {}; + } + + UniqueFD allocFd(alloc.fd); + ret = ::ioctl(allocFd.get(), DMA_BUF_SET_NAME, name); + if (ret < 0) { + LOG(RPI, Error) << "dmaHeap naming failure for " + << name; + return {}; + } + + return allocFd; +} + +} /* namespace RPi */ + +} /* namespace libcamera */ diff --git a/src/libcamera/pipeline/rpi/vc4/dma_heaps.h b/src/libcamera/pipeline/rpi/vc4/dma_heaps.h new file mode 100644 index 00000000..0a4a8d86 --- /dev/null +++ b/src/libcamera/pipeline/rpi/vc4/dma_heaps.h @@ -0,0 +1,32 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2020, Raspberry Pi Ltd + * + * dma_heaps.h - Helper class for dma-heap allocations. + */ + +#pragma once + +#include + +#include + +namespace libcamera { + +namespace RPi { + +class DmaHeap +{ +public: + DmaHeap(); + ~DmaHeap(); + bool isValid() const { return dmaHeapHandle_.isValid(); } + UniqueFD alloc(const char *name, std::size_t size); + +private: + UniqueFD dmaHeapHandle_; +}; + +} /* namespace RPi */ + +} /* namespace libcamera */ diff --git a/src/libcamera/pipeline/rpi/vc4/meson.build b/src/libcamera/pipeline/rpi/vc4/meson.build new file mode 100644 index 00000000..228823f3 --- /dev/null +++ b/src/libcamera/pipeline/rpi/vc4/meson.build @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: CC0-1.0 + +libcamera_sources += files([ + 'dma_heaps.cpp', + 'raspberrypi.cpp', +]) + +subdir('data') diff --git a/src/libcamera/pipeline/rpi/vc4/raspberrypi.cpp b/src/libcamera/pipeline/rpi/vc4/raspberrypi.cpp new file mode 100644 index 00000000..af464d15 --- /dev/null +++ b/src/libcamera/pipeline/rpi/vc4/raspberrypi.cpp @@ -0,0 +1,2433 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2019-2021, Raspberry Pi Ltd + * + * raspberrypi.cpp - Pipeline handler for VC4-based Raspberry Pi devices + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "libcamera/internal/bayer_format.h" +#include "libcamera/internal/camera.h" +#include "libcamera/internal/camera_lens.h" +#include "libcamera/internal/camera_sensor.h" +#include "libcamera/internal/device_enumerator.h" +#include "libcamera/internal/framebuffer.h" +#include "libcamera/internal/ipa_manager.h" +#include "libcamera/internal/media_device.h" +#include "libcamera/internal/pipeline_handler.h" +#include "libcamera/internal/v4l2_videodevice.h" +#include "libcamera/internal/yaml_parser.h" + +#include "../common/delayed_controls.h" +#include "../common/rpi_stream.h" +#include "dma_heaps.h" + +using namespace std::chrono_literals; + +namespace libcamera { + +LOG_DEFINE_CATEGORY(RPI) + +namespace { + +constexpr unsigned int defaultRawBitDepth = 12; + +/* Map of mbus codes to supported sizes reported by the sensor. */ +using SensorFormats = std::map>; + +SensorFormats populateSensorFormats(std::unique_ptr &sensor) +{ + SensorFormats formats; + + for (auto const mbusCode : sensor->mbusCodes()) + formats.emplace(mbusCode, sensor->sizes(mbusCode)); + + return formats; +} + +bool isMonoSensor(std::unique_ptr &sensor) +{ + unsigned int mbusCode = sensor->mbusCodes()[0]; + const BayerFormat &bayer = BayerFormat::fromMbusCode(mbusCode); + + return bayer.order == BayerFormat::Order::MONO; +} + +PixelFormat mbusCodeToPixelFormat(unsigned int mbus_code, + BayerFormat::Packing packingReq) +{ + BayerFormat bayer = BayerFormat::fromMbusCode(mbus_code); + + ASSERT(bayer.isValid()); + + bayer.packing = packingReq; + PixelFormat pix = bayer.toPixelFormat(); + + /* + * Not all formats (e.g. 8-bit or 16-bit Bayer formats) can have packed + * variants. So if the PixelFormat returns as invalid, use the non-packed + * conversion instead. + */ + if (!pix.isValid()) { + bayer.packing = BayerFormat::Packing::None; + pix = bayer.toPixelFormat(); + } + + return pix; +} + +V4L2DeviceFormat toV4L2DeviceFormat(const V4L2VideoDevice *dev, + const V4L2SubdeviceFormat &format, + BayerFormat::Packing packingReq) +{ + const PixelFormat pix = mbusCodeToPixelFormat(format.mbus_code, packingReq); + V4L2DeviceFormat deviceFormat; + + deviceFormat.fourcc = dev->toV4L2PixelFormat(pix); + deviceFormat.size = format.size; + deviceFormat.colorSpace = format.colorSpace; + return deviceFormat; +} + +bool isRaw(const PixelFormat &pixFmt) +{ + /* This test works for both Bayer and raw mono formats. */ + return BayerFormat::fromPixelFormat(pixFmt).isValid(); +} + +double scoreFormat(double desired, double actual) +{ + double score = desired - actual; + /* Smaller desired dimensions are preferred. */ + if (score < 0.0) + score = (-score) / 8; + /* Penalise non-exact matches. */ + if (actual != desired) + score *= 2; + + return score; +} + +V4L2SubdeviceFormat findBestFormat(const SensorFormats &formatsMap, const Size &req, unsigned int bitDepth) +{ + double bestScore = std::numeric_limits::max(), score; + V4L2SubdeviceFormat bestFormat; + bestFormat.colorSpace = ColorSpace::Raw; + + constexpr float penaltyAr = 1500.0; + constexpr float penaltyBitDepth = 500.0; + + /* Calculate the closest/best mode from the user requested size. */ + for (const auto &iter : formatsMap) { + const unsigned int mbusCode = iter.first; + const PixelFormat format = mbusCodeToPixelFormat(mbusCode, + BayerFormat::Packing::None); + const PixelFormatInfo &info = PixelFormatInfo::info(format); + + for (const Size &size : iter.second) { + double reqAr = static_cast(req.width) / req.height; + double fmtAr = static_cast(size.width) / size.height; + + /* Score the dimensions for closeness. */ + score = scoreFormat(req.width, size.width); + score += scoreFormat(req.height, size.height); + score += penaltyAr * scoreFormat(reqAr, fmtAr); + + /* Add any penalties... this is not an exact science! */ + score += utils::abs_diff(info.bitsPerPixel, bitDepth) * penaltyBitDepth; + + if (score <= bestScore) { + bestScore = score; + bestFormat.mbus_code = mbusCode; + bestFormat.size = size; + } + + LOG(RPI, Debug) << "Format: " << size + << " fmt " << format + << " Score: " << score + << " (best " << bestScore << ")"; + } + } + + return bestFormat; +} + +enum class Unicam : unsigned int { Image, Embedded }; +enum class Isp : unsigned int { Input, Output0, Output1, Stats }; + +} /* namespace */ + +class RPiCameraData : public Camera::Private +{ +public: + RPiCameraData(PipelineHandler *pipe) + : Camera::Private(pipe), state_(State::Stopped), + flipsAlterBayerOrder_(false), dropFrameCount_(0), + buffersAllocated_(false), ispOutputCount_(0) + { + } + + ~RPiCameraData() + { + freeBuffers(); + } + + void freeBuffers(); + void frameStarted(uint32_t sequence); + + int loadIPA(ipa::RPi::IPAInitResult *result); + int configureIPA(const CameraConfiguration *config, ipa::RPi::IPAConfigResult *result); + int loadPipelineConfiguration(); + + void enumerateVideoDevices(MediaLink *link); + + void statsMetadataComplete(uint32_t bufferId, const ControlList &controls); + void runIsp(uint32_t bufferId); + void embeddedComplete(uint32_t bufferId); + void setIspControls(const ControlList &controls); + void setDelayedControls(const ControlList &controls, uint32_t delayContext); + void setLensControls(const ControlList &controls); + void setCameraTimeout(uint32_t maxExposureTimeMs); + void setSensorControls(ControlList &controls); + void unicamTimeout(); + + /* bufferComplete signal handlers. */ + void unicamBufferDequeue(FrameBuffer *buffer); + void ispInputDequeue(FrameBuffer *buffer); + void ispOutputDequeue(FrameBuffer *buffer); + + void clearIncompleteRequests(); + void handleStreamBuffer(FrameBuffer *buffer, RPi::Stream *stream); + void handleExternalBuffer(FrameBuffer *buffer, RPi::Stream *stream); + void handleState(); + Rectangle scaleIspCrop(const Rectangle &ispCrop) const; + void applyScalerCrop(const ControlList &controls); + + std::unique_ptr ipa_; + + std::unique_ptr sensor_; + SensorFormats sensorFormats_; + /* Array of Unicam and ISP device streams and associated buffers/streams. */ + RPi::Device unicam_; + RPi::Device isp_; + /* The vector below is just for convenience when iterating over all streams. */ + std::vector streams_; + /* Stores the ids of the buffers mapped in the IPA. */ + std::unordered_set ipaBuffers_; + /* + * Stores a cascade of Video Mux or Bridge devices between the sensor and + * Unicam together with media link across the entities. + */ + std::vector, MediaLink *>> bridgeDevices_; + + /* DMAHEAP allocation helper. */ + RPi::DmaHeap dmaHeap_; + SharedFD lsTable_; + + std::unique_ptr delayedCtrls_; + bool sensorMetadata_; + + /* + * All the functions in this class are called from a single calling + * thread. So, we do not need to have any mutex to protect access to any + * of the variables below. + */ + enum class State { Stopped, Idle, Busy, IpaComplete, Error }; + State state_; + + bool isRunning() + { + return state_ != State::Stopped && state_ != State::Error; + } + + struct BayerFrame { + FrameBuffer *buffer; + ControlList controls; + unsigned int delayContext; + }; + + std::queue bayerQueue_; + std::queue embeddedQueue_; + std::deque requestQueue_; + + /* + * Store the "native" Bayer order (that is, with no transforms + * applied). + */ + bool flipsAlterBayerOrder_; + BayerFormat::Order nativeBayerOrder_; + + /* For handling digital zoom. */ + IPACameraSensorInfo sensorInfo_; + Rectangle ispCrop_; /* crop in ISP (camera mode) pixels */ + Rectangle scalerCrop_; /* crop in sensor native pixels */ + Size ispMinCropSize_; + + unsigned int dropFrameCount_; + + /* + * If set, this stores the value that represets a gain of one for + * the V4L2_CID_NOTIFY_GAINS control. + */ + std::optional notifyGainsUnity_; + + /* Have internal buffers been allocated? */ + bool buffersAllocated_; + + struct Config { + /* + * The minimum number of internal buffers to be allocated for + * the Unicam Image stream. + */ + unsigned int minUnicamBuffers; + /* + * The minimum total (internal + external) buffer count used for + * the Unicam Image stream. + * + * Note that: + * minTotalUnicamBuffers must be >= 1, and + * minTotalUnicamBuffers >= minUnicamBuffers + */ + unsigned int minTotalUnicamBuffers; + /* + * Override any request from the IPA to drop a number of startup + * frames. + */ + bool disableStartupFrameDrops; + /* + * Override the Unicam timeout value calculated by the IPA based + * on frame durations. + */ + unsigned int unicamTimeoutValue; + }; + + Config config_; + +private: + void checkRequestCompleted(); + void fillRequestMetadata(const ControlList &bufferControls, + Request *request); + void tryRunPipeline(); + bool findMatchingBuffers(BayerFrame &bayerFrame, FrameBuffer *&embeddedBuffer); + + unsigned int ispOutputCount_; +}; + +class RPiCameraConfiguration : public CameraConfiguration +{ +public: + RPiCameraConfiguration(const RPiCameraData *data); + + CameraConfiguration::Status validateColorSpaces(ColorSpaceFlags flags); + Status validate() override; + + /* Cache the combinedTransform_ that will be applied to the sensor */ + Transform combinedTransform_; + +private: + const RPiCameraData *data_; + + /* + * Store the colour spaces that all our streams will have. RGB format streams + * will have the same colorspace as YUV streams, with YCbCr field cleared and + * range set to full. + */ + std::optional yuvColorSpace_; + std::optional rgbColorSpace_; +}; + +class PipelineHandlerRPi : public PipelineHandler +{ +public: + PipelineHandlerRPi(CameraManager *manager); + + std::unique_ptr generateConfiguration(Camera *camera, + const StreamRoles &roles) override; + int configure(Camera *camera, CameraConfiguration *config) override; + + int exportFrameBuffers(Camera *camera, Stream *stream, + std::vector> *buffers) override; + + int start(Camera *camera, const ControlList *controls) override; + void stopDevice(Camera *camera) override; + + int queueRequestDevice(Camera *camera, Request *request) override; + + bool match(DeviceEnumerator *enumerator) override; + + void releaseDevice(Camera *camera) override; + +private: + RPiCameraData *cameraData(Camera *camera) + { + return static_cast(camera->_d()); + } + + int registerCamera(MediaDevice *unicam, MediaDevice *isp, MediaEntity *sensorEntity); + int queueAllBuffers(Camera *camera); + int prepareBuffers(Camera *camera); + void mapBuffers(Camera *camera, const RPi::BufferMap &buffers, unsigned int mask); +}; + +RPiCameraConfiguration::RPiCameraConfiguration(const RPiCameraData *data) + : CameraConfiguration(), data_(data) +{ +} + +static const std::vector validColorSpaces = { + ColorSpace::Sycc, + ColorSpace::Smpte170m, + ColorSpace::Rec709 +}; + +static std::optional findValidColorSpace(const ColorSpace &colourSpace) +{ + for (auto cs : validColorSpaces) { + if (colourSpace.primaries == cs.primaries && + colourSpace.transferFunction == cs.transferFunction) + return cs; + } + + return std::nullopt; +} + +static bool isRgb(const PixelFormat &pixFmt) +{ + const PixelFormatInfo &info = PixelFormatInfo::info(pixFmt); + return info.colourEncoding == PixelFormatInfo::ColourEncodingRGB; +} + +static bool isYuv(const PixelFormat &pixFmt) +{ + /* The code below would return true for raw mono streams, so weed those out first. */ + if (isRaw(pixFmt)) + return false; + + const PixelFormatInfo &info = PixelFormatInfo::info(pixFmt); + return info.colourEncoding == PixelFormatInfo::ColourEncodingYUV; +} + +/* + * Raspberry Pi drivers expect the following colour spaces: + * - V4L2_COLORSPACE_RAW for raw streams. + * - One of V4L2_COLORSPACE_JPEG, V4L2_COLORSPACE_SMPTE170M, V4L2_COLORSPACE_REC709 for + * non-raw streams. Other fields such as transfer function, YCbCr encoding and + * quantisation are not used. + * + * The libcamera colour spaces that we wish to use corresponding to these are therefore: + * - ColorSpace::Raw for V4L2_COLORSPACE_RAW + * - ColorSpace::Sycc for V4L2_COLORSPACE_JPEG + * - ColorSpace::Smpte170m for V4L2_COLORSPACE_SMPTE170M + * - ColorSpace::Rec709 for V4L2_COLORSPACE_REC709 + */ + +CameraConfiguration::Status RPiCameraConfiguration::validateColorSpaces([[maybe_unused]] ColorSpaceFlags flags) +{ + Status status = Valid; + yuvColorSpace_.reset(); + + for (auto cfg : config_) { + /* First fix up raw streams to have the "raw" colour space. */ + if (isRaw(cfg.pixelFormat)) { + /* If there was no value here, that doesn't count as "adjusted". */ + if (cfg.colorSpace && cfg.colorSpace != ColorSpace::Raw) + status = Adjusted; + cfg.colorSpace = ColorSpace::Raw; + continue; + } + + /* Next we need to find our shared colour space. The first valid one will do. */ + if (cfg.colorSpace && !yuvColorSpace_) + yuvColorSpace_ = findValidColorSpace(cfg.colorSpace.value()); + } + + /* If no colour space was given anywhere, choose sYCC. */ + if (!yuvColorSpace_) + yuvColorSpace_ = ColorSpace::Sycc; + + /* Note the version of this that any RGB streams will have to use. */ + rgbColorSpace_ = yuvColorSpace_; + rgbColorSpace_->ycbcrEncoding = ColorSpace::YcbcrEncoding::None; + rgbColorSpace_->range = ColorSpace::Range::Full; + + /* Go through the streams again and force everyone to the same colour space. */ + for (auto cfg : config_) { + if (cfg.colorSpace == ColorSpace::Raw) + continue; + + if (isYuv(cfg.pixelFormat) && cfg.colorSpace != yuvColorSpace_) { + /* Again, no value means "not adjusted". */ + if (cfg.colorSpace) + status = Adjusted; + cfg.colorSpace = yuvColorSpace_; + } + if (isRgb(cfg.pixelFormat) && cfg.colorSpace != rgbColorSpace_) { + /* Be nice, and let the YUV version count as non-adjusted too. */ + if (cfg.colorSpace && cfg.colorSpace != yuvColorSpace_) + status = Adjusted; + cfg.colorSpace = rgbColorSpace_; + } + } + + return status; +} + +CameraConfiguration::Status RPiCameraConfiguration::validate() +{ + Status status = Valid; + + if (config_.empty()) + return Invalid; + + status = validateColorSpaces(ColorSpaceFlag::StreamsShareColorSpace); + + /* + * Validate the requested transform against the sensor capabilities and + * rotation and store the final combined transform that configure() will + * need to apply to the sensor to save us working it out again. + */ + Transform requestedTransform = transform; + combinedTransform_ = data_->sensor_->validateTransform(&transform); + if (transform != requestedTransform) + status = Adjusted; + + unsigned int rawCount = 0, outCount = 0, count = 0, maxIndex = 0; + std::pair outSize[2]; + Size maxSize; + for (StreamConfiguration &cfg : config_) { + if (isRaw(cfg.pixelFormat)) { + /* + * Calculate the best sensor mode we can use based on + * the user request. + */ + V4L2VideoDevice *unicam = data_->unicam_[Unicam::Image].dev(); + const PixelFormatInfo &info = PixelFormatInfo::info(cfg.pixelFormat); + unsigned int bitDepth = info.isValid() ? info.bitsPerPixel : defaultRawBitDepth; + V4L2SubdeviceFormat sensorFormat = findBestFormat(data_->sensorFormats_, cfg.size, bitDepth); + BayerFormat::Packing packing = BayerFormat::Packing::CSI2; + if (info.isValid() && !info.packed) + packing = BayerFormat::Packing::None; + V4L2DeviceFormat unicamFormat = toV4L2DeviceFormat(unicam, sensorFormat, packing); + int ret = unicam->tryFormat(&unicamFormat); + if (ret) + return Invalid; + + /* + * Some sensors change their Bayer order when they are + * h-flipped or v-flipped, according to the transform. + * If this one does, we must advertise the transformed + * Bayer order in the raw stream. Note how we must + * fetch the "native" (i.e. untransformed) Bayer order, + * because the sensor may currently be flipped! + */ + V4L2PixelFormat fourcc = unicamFormat.fourcc; + if (data_->flipsAlterBayerOrder_) { + BayerFormat bayer = BayerFormat::fromV4L2PixelFormat(fourcc); + bayer.order = data_->nativeBayerOrder_; + bayer = bayer.transform(combinedTransform_); + fourcc = bayer.toV4L2PixelFormat(); + } + + PixelFormat unicamPixFormat = fourcc.toPixelFormat(); + if (cfg.size != unicamFormat.size || + cfg.pixelFormat != unicamPixFormat) { + cfg.size = unicamFormat.size; + cfg.pixelFormat = unicamPixFormat; + status = Adjusted; + } + + cfg.stride = unicamFormat.planes[0].bpl; + cfg.frameSize = unicamFormat.planes[0].size; + + rawCount++; + } else { + outSize[outCount] = std::make_pair(count, cfg.size); + /* Record the largest resolution for fixups later. */ + if (maxSize < cfg.size) { + maxSize = cfg.size; + maxIndex = outCount; + } + outCount++; + } + + count++; + + /* Can only output 1 RAW stream, or 2 YUV/RGB streams. */ + if (rawCount > 1 || outCount > 2) { + LOG(RPI, Error) << "Invalid number of streams requested"; + return Invalid; + } + } + + /* + * Now do any fixups needed. For the two ISP outputs, one stream must be + * equal or smaller than the other in all dimensions. + */ + for (unsigned int i = 0; i < outCount; i++) { + outSize[i].second.width = std::min(outSize[i].second.width, + maxSize.width); + outSize[i].second.height = std::min(outSize[i].second.height, + maxSize.height); + + if (config_.at(outSize[i].first).size != outSize[i].second) { + config_.at(outSize[i].first).size = outSize[i].second; + status = Adjusted; + } + + /* + * Also validate the correct pixel formats here. + * Note that Output0 and Output1 support a different + * set of formats. + * + * Output 0 must be for the largest resolution. We will + * have that fixed up in the code above. + * + */ + StreamConfiguration &cfg = config_.at(outSize[i].first); + PixelFormat &cfgPixFmt = cfg.pixelFormat; + V4L2VideoDevice *dev; + + if (i == maxIndex) + dev = data_->isp_[Isp::Output0].dev(); + else + dev = data_->isp_[Isp::Output1].dev(); + + V4L2VideoDevice::Formats fmts = dev->formats(); + + if (fmts.find(dev->toV4L2PixelFormat(cfgPixFmt)) == fmts.end()) { + /* If we cannot find a native format, use a default one. */ + cfgPixFmt = formats::NV12; + status = Adjusted; + } + + V4L2DeviceFormat format; + format.fourcc = dev->toV4L2PixelFormat(cfg.pixelFormat); + format.size = cfg.size; + /* We want to send the associated YCbCr info through to the driver. */ + format.colorSpace = yuvColorSpace_; + + LOG(RPI, Debug) + << "Try color space " << ColorSpace::toString(cfg.colorSpace); + + int ret = dev->tryFormat(&format); + if (ret) + return Invalid; + + /* + * But for RGB streams, the YCbCr info gets overwritten on the way back + * so we must check against what the stream cfg says, not what we actually + * requested (which carefully included the YCbCr info)! + */ + if (cfg.colorSpace != format.colorSpace) { + status = Adjusted; + LOG(RPI, Debug) + << "Color space changed from " + << ColorSpace::toString(cfg.colorSpace) << " to " + << ColorSpace::toString(format.colorSpace); + } + + cfg.colorSpace = format.colorSpace; + + cfg.stride = format.planes[0].bpl; + cfg.frameSize = format.planes[0].size; + + } + + return status; +} + +PipelineHandlerRPi::PipelineHandlerRPi(CameraManager *manager) + : PipelineHandler(manager) +{ +} + +std::unique_ptr +PipelineHandlerRPi::generateConfiguration(Camera *camera, const StreamRoles &roles) +{ + RPiCameraData *data = cameraData(camera); + std::unique_ptr config = + std::make_unique(data); + V4L2SubdeviceFormat sensorFormat; + unsigned int bufferCount; + PixelFormat pixelFormat; + V4L2VideoDevice::Formats fmts; + Size size; + std::optional colorSpace; + + if (roles.empty()) + return config; + + unsigned int rawCount = 0; + unsigned int outCount = 0; + Size sensorSize = data->sensor_->resolution(); + for (const StreamRole role : roles) { + switch (role) { + case StreamRole::Raw: + size = sensorSize; + sensorFormat = findBestFormat(data->sensorFormats_, size, defaultRawBitDepth); + pixelFormat = mbusCodeToPixelFormat(sensorFormat.mbus_code, + BayerFormat::Packing::CSI2); + ASSERT(pixelFormat.isValid()); + colorSpace = ColorSpace::Raw; + bufferCount = 2; + rawCount++; + break; + + case StreamRole::StillCapture: + fmts = data->isp_[Isp::Output0].dev()->formats(); + pixelFormat = formats::NV12; + /* + * Still image codecs usually expect the sYCC color space. + * Even RGB codecs will be fine as the RGB we get with the + * sYCC color space is the same as sRGB. + */ + colorSpace = ColorSpace::Sycc; + /* Return the largest sensor resolution. */ + size = sensorSize; + bufferCount = 1; + outCount++; + break; + + case StreamRole::VideoRecording: + /* + * The colour denoise algorithm requires the analysis + * image, produced by the second ISP output, to be in + * YUV420 format. Select this format as the default, to + * maximize chances that it will be picked by + * applications and enable usage of the colour denoise + * algorithm. + */ + fmts = data->isp_[Isp::Output0].dev()->formats(); + pixelFormat = formats::YUV420; + /* + * Choose a color space appropriate for video recording. + * Rec.709 will be a good default for HD resolutions. + */ + colorSpace = ColorSpace::Rec709; + size = { 1920, 1080 }; + bufferCount = 4; + outCount++; + break; + + case StreamRole::Viewfinder: + fmts = data->isp_[Isp::Output0].dev()->formats(); + pixelFormat = formats::ARGB8888; + colorSpace = ColorSpace::Sycc; + size = { 800, 600 }; + bufferCount = 4; + outCount++; + break; + + default: + LOG(RPI, Error) << "Requested stream role not supported: " + << role; + return nullptr; + } + + if (rawCount > 1 || outCount > 2) { + LOG(RPI, Error) << "Invalid stream roles requested"; + return nullptr; + } + + std::map> deviceFormats; + if (role == StreamRole::Raw) { + /* Translate the MBUS codes to a PixelFormat. */ + for (const auto &format : data->sensorFormats_) { + PixelFormat pf = mbusCodeToPixelFormat(format.first, + BayerFormat::Packing::CSI2); + if (pf.isValid()) + deviceFormats.emplace(std::piecewise_construct, std::forward_as_tuple(pf), + std::forward_as_tuple(format.second.begin(), format.second.end())); + } + } else { + /* + * Translate the V4L2PixelFormat to PixelFormat. Note that we + * limit the recommended largest ISP output size to match the + * sensor resolution. + */ + for (const auto &format : fmts) { + PixelFormat pf = format.first.toPixelFormat(); + if (pf.isValid()) { + const SizeRange &ispSizes = format.second[0]; + deviceFormats[pf].emplace_back(ispSizes.min, sensorSize, + ispSizes.hStep, ispSizes.vStep); + } + } + } + + /* Add the stream format based on the device node used for the use case. */ + StreamFormats formats(deviceFormats); + StreamConfiguration cfg(formats); + cfg.size = size; + cfg.pixelFormat = pixelFormat; + cfg.colorSpace = colorSpace; + cfg.bufferCount = bufferCount; + config->addConfiguration(cfg); + } + + config->validate(); + + return config; +} + +int PipelineHandlerRPi::configure(Camera *camera, CameraConfiguration *config) +{ + RPiCameraData *data = cameraData(camera); + int ret; + + /* Start by freeing all buffers and reset the Unicam and ISP stream states. */ + data->freeBuffers(); + for (auto const stream : data->streams_) + stream->setExternal(false); + + BayerFormat::Packing packing = BayerFormat::Packing::CSI2; + Size maxSize, sensorSize; + unsigned int maxIndex = 0; + bool rawStream = false; + unsigned int bitDepth = defaultRawBitDepth; + + /* + * Look for the RAW stream (if given) size as well as the largest + * ISP output size. + */ + for (unsigned i = 0; i < config->size(); i++) { + StreamConfiguration &cfg = config->at(i); + + if (isRaw(cfg.pixelFormat)) { + /* + * If we have been given a RAW stream, use that size + * for setting up the sensor. + */ + sensorSize = cfg.size; + rawStream = true; + /* Check if the user has explicitly set an unpacked format. */ + BayerFormat bayerFormat = BayerFormat::fromPixelFormat(cfg.pixelFormat); + packing = bayerFormat.packing; + bitDepth = bayerFormat.bitDepth; + } else { + if (cfg.size > maxSize) { + maxSize = config->at(i).size; + maxIndex = i; + } + } + } + + /* + * Calculate the best sensor mode we can use based on the user's + * request, and apply it to the sensor with the cached transform, if + * any. + */ + V4L2SubdeviceFormat sensorFormat = findBestFormat(data->sensorFormats_, rawStream ? sensorSize : maxSize, bitDepth); + const RPiCameraConfiguration *rpiConfig = static_cast(config); + ret = data->sensor_->setFormat(&sensorFormat, rpiConfig->combinedTransform_); + if (ret) + return ret; + + V4L2VideoDevice *unicam = data->unicam_[Unicam::Image].dev(); + V4L2DeviceFormat unicamFormat = toV4L2DeviceFormat(unicam, sensorFormat, packing); + ret = unicam->setFormat(&unicamFormat); + if (ret) + return ret; + + LOG(RPI, Info) << "Sensor: " << camera->id() + << " - Selected sensor format: " << sensorFormat + << " - Selected unicam format: " << unicamFormat; + + ret = data->isp_[Isp::Input].dev()->setFormat(&unicamFormat); + if (ret) + return ret; + + /* + * See which streams are requested, and route the user + * StreamConfiguration appropriately. + */ + V4L2DeviceFormat format; + bool output0Set = false, output1Set = false; + for (unsigned i = 0; i < config->size(); i++) { + StreamConfiguration &cfg = config->at(i); + + if (isRaw(cfg.pixelFormat)) { + cfg.setStream(&data->unicam_[Unicam::Image]); + data->unicam_[Unicam::Image].setExternal(true); + continue; + } + + /* The largest resolution gets routed to the ISP Output 0 node. */ + RPi::Stream *stream = i == maxIndex ? &data->isp_[Isp::Output0] + : &data->isp_[Isp::Output1]; + + V4L2PixelFormat fourcc = stream->dev()->toV4L2PixelFormat(cfg.pixelFormat); + format.size = cfg.size; + format.fourcc = fourcc; + format.colorSpace = cfg.colorSpace; + + LOG(RPI, Debug) << "Setting " << stream->name() << " to " + << format; + + ret = stream->dev()->setFormat(&format); + if (ret) + return -EINVAL; + + if (format.size != cfg.size || format.fourcc != fourcc) { + LOG(RPI, Error) + << "Failed to set requested format on " << stream->name() + << ", returned " << format; + return -EINVAL; + } + + LOG(RPI, Debug) + << "Stream " << stream->name() << " has color space " + << ColorSpace::toString(cfg.colorSpace); + + cfg.setStream(stream); + stream->setExternal(true); + + if (i != maxIndex) + output1Set = true; + else + output0Set = true; + } + + /* + * If ISP::Output0 stream has not been configured by the application, + * we must allow the hardware to generate an output so that the data + * flow in the pipeline handler remains consistent, and we still generate + * statistics for the IPA to use. So enable the output at a very low + * resolution for internal use. + * + * \todo Allow the pipeline to work correctly without Output0 and only + * statistics coming from the hardware. + */ + if (!output0Set) { + V4L2VideoDevice *dev = data->isp_[Isp::Output0].dev(); + + maxSize = Size(320, 240); + format = {}; + format.size = maxSize; + format.fourcc = dev->toV4L2PixelFormat(formats::YUV420); + /* No one asked for output, so the color space doesn't matter. */ + format.colorSpace = ColorSpace::Sycc; + ret = dev->setFormat(&format); + if (ret) { + LOG(RPI, Error) + << "Failed to set default format on ISP Output0: " + << ret; + return -EINVAL; + } + + LOG(RPI, Debug) << "Defaulting ISP Output0 format to " + << format; + } + + /* + * If ISP::Output1 stream has not been requested by the application, we + * set it up for internal use now. This second stream will be used for + * fast colour denoise, and must be a quarter resolution of the ISP::Output0 + * stream. However, also limit the maximum size to 1200 pixels in the + * larger dimension, just to avoid being wasteful with buffer allocations + * and memory bandwidth. + * + * \todo If Output 1 format is not YUV420, Output 1 ought to be disabled as + * colour denoise will not run. + */ + if (!output1Set) { + V4L2VideoDevice *dev = data->isp_[Isp::Output1].dev(); + + V4L2DeviceFormat output1Format; + constexpr Size maxDimensions(1200, 1200); + const Size limit = maxDimensions.boundedToAspectRatio(format.size); + + output1Format.size = (format.size / 2).boundedTo(limit).alignedDownTo(2, 2); + output1Format.colorSpace = format.colorSpace; + output1Format.fourcc = dev->toV4L2PixelFormat(formats::YUV420); + + LOG(RPI, Debug) << "Setting ISP Output1 (internal) to " + << output1Format; + + ret = dev->setFormat(&output1Format); + if (ret) { + LOG(RPI, Error) << "Failed to set format on ISP Output1: " + << ret; + return -EINVAL; + } + } + + /* ISP statistics output format. */ + format = {}; + format.fourcc = V4L2PixelFormat(V4L2_META_FMT_BCM2835_ISP_STATS); + ret = data->isp_[Isp::Stats].dev()->setFormat(&format); + if (ret) { + LOG(RPI, Error) << "Failed to set format on ISP stats stream: " + << format; + return ret; + } + + /* Figure out the smallest selection the ISP will allow. */ + Rectangle testCrop(0, 0, 1, 1); + data->isp_[Isp::Input].dev()->setSelection(V4L2_SEL_TGT_CROP, &testCrop); + data->ispMinCropSize_ = testCrop.size(); + + /* Adjust aspect ratio by providing crops on the input image. */ + Size size = unicamFormat.size.boundedToAspectRatio(maxSize); + Rectangle crop = size.centeredTo(Rectangle(unicamFormat.size).center()); + Rectangle defaultCrop = crop; + data->ispCrop_ = crop; + + data->isp_[Isp::Input].dev()->setSelection(V4L2_SEL_TGT_CROP, &crop); + + ipa::RPi::IPAConfigResult result; + ret = data->configureIPA(config, &result); + if (ret) + LOG(RPI, Error) << "Failed to configure the IPA: " << ret; + + /* + * Set the scaler crop to the value we are using (scaled to native sensor + * coordinates). + */ + data->scalerCrop_ = data->scaleIspCrop(data->ispCrop_); + + /* + * Configure the Unicam embedded data output format only if the sensor + * supports it. + */ + if (data->sensorMetadata_) { + V4L2SubdeviceFormat embeddedFormat; + + data->sensor_->device()->getFormat(1, &embeddedFormat); + format.fourcc = V4L2PixelFormat(V4L2_META_FMT_SENSOR_DATA); + format.planes[0].size = embeddedFormat.size.width * embeddedFormat.size.height; + + LOG(RPI, Debug) << "Setting embedded data format."; + ret = data->unicam_[Unicam::Embedded].dev()->setFormat(&format); + if (ret) { + LOG(RPI, Error) << "Failed to set format on Unicam embedded: " + << format; + return ret; + } + } + + /* + * Update the ScalerCropMaximum to the correct value for this camera mode. + * For us, it's the same as the "analogue crop". + * + * \todo Make this property the ScalerCrop maximum value when dynamic + * controls are available and set it at validate() time + */ + data->properties_.set(properties::ScalerCropMaximum, data->sensorInfo_.analogCrop); + + /* Store the mode sensitivity for the application. */ + data->properties_.set(properties::SensorSensitivity, result.modeSensitivity); + + /* Update the controls that the Raspberry Pi IPA can handle. */ + ControlInfoMap::Map ctrlMap; + for (auto const &c : result.controlInfo) + ctrlMap.emplace(c.first, c.second); + + /* Add the ScalerCrop control limits based on the current mode. */ + Rectangle ispMinCrop = data->scaleIspCrop(Rectangle(data->ispMinCropSize_)); + defaultCrop = data->scaleIspCrop(defaultCrop); + ctrlMap[&controls::ScalerCrop] = ControlInfo(ispMinCrop, data->sensorInfo_.analogCrop, defaultCrop); + + data->controlInfo_ = ControlInfoMap(std::move(ctrlMap), result.controlInfo.idmap()); + + /* Setup the Video Mux/Bridge entities. */ + for (auto &[device, link] : data->bridgeDevices_) { + /* + * Start by disabling all the sink pad links on the devices in the + * cascade, with the exception of the link connecting the device. + */ + for (const MediaPad *p : device->entity()->pads()) { + if (!(p->flags() & MEDIA_PAD_FL_SINK)) + continue; + + for (MediaLink *l : p->links()) { + if (l != link) + l->setEnabled(false); + } + } + + /* + * Next, enable the entity -> entity links, and setup the pad format. + * + * \todo Some bridge devices may chainge the media bus code, so we + * ought to read the source pad format and propagate it to the sink pad. + */ + link->setEnabled(true); + const MediaPad *sinkPad = link->sink(); + ret = device->setFormat(sinkPad->index(), &sensorFormat); + if (ret) { + LOG(RPI, Error) << "Failed to set format on " << device->entity()->name() + << " pad " << sinkPad->index() + << " with format " << format + << ": " << ret; + return ret; + } + + LOG(RPI, Debug) << "Configured media link on device " << device->entity()->name() + << " on pad " << sinkPad->index(); + } + + return ret; +} + +int PipelineHandlerRPi::exportFrameBuffers([[maybe_unused]] Camera *camera, Stream *stream, + std::vector> *buffers) +{ + RPi::Stream *s = static_cast(stream); + unsigned int count = stream->configuration().bufferCount; + int ret = s->dev()->exportBuffers(count, buffers); + + s->setExportedBuffers(buffers); + + return ret; +} + +int PipelineHandlerRPi::start(Camera *camera, const ControlList *controls) +{ + RPiCameraData *data = cameraData(camera); + int ret; + + /* Check if a ScalerCrop control was specified. */ + if (controls) + data->applyScalerCrop(*controls); + + /* Start the IPA. */ + ipa::RPi::StartConfig startConfig; + data->ipa_->start(controls ? *controls : ControlList{ controls::controls }, + &startConfig); + + /* Apply any gain/exposure settings that the IPA may have passed back. */ + if (!startConfig.controls.empty()) + data->setSensorControls(startConfig.controls); + + /* Configure the number of dropped frames required on startup. */ + data->dropFrameCount_ = data->config_.disableStartupFrameDrops + ? 0 : startConfig.dropFrameCount; + + for (auto const stream : data->streams_) + stream->resetBuffers(); + + if (!data->buffersAllocated_) { + /* Allocate buffers for internal pipeline usage. */ + ret = prepareBuffers(camera); + if (ret) { + LOG(RPI, Error) << "Failed to allocate buffers"; + data->freeBuffers(); + stop(camera); + return ret; + } + data->buffersAllocated_ = true; + } + + /* We need to set the dropFrameCount_ before queueing buffers. */ + ret = queueAllBuffers(camera); + if (ret) { + LOG(RPI, Error) << "Failed to queue buffers"; + stop(camera); + return ret; + } + + /* Enable SOF event generation. */ + data->unicam_[Unicam::Image].dev()->setFrameStartEnabled(true); + + /* + * Reset the delayed controls with the gain and exposure values set by + * the IPA. + */ + data->delayedCtrls_->reset(0); + + data->state_ = RPiCameraData::State::Idle; + + /* Start all streams. */ + for (auto const stream : data->streams_) { + ret = stream->dev()->streamOn(); + if (ret) { + stop(camera); + return ret; + } + } + + return 0; +} + +void PipelineHandlerRPi::stopDevice(Camera *camera) +{ + RPiCameraData *data = cameraData(camera); + + data->state_ = RPiCameraData::State::Stopped; + + /* Disable SOF event generation. */ + data->unicam_[Unicam::Image].dev()->setFrameStartEnabled(false); + + for (auto const stream : data->streams_) + stream->dev()->streamOff(); + + data->clearIncompleteRequests(); + data->bayerQueue_ = {}; + data->embeddedQueue_ = {}; + + /* Stop the IPA. */ + data->ipa_->stop(); +} + +int PipelineHandlerRPi::queueRequestDevice(Camera *camera, Request *request) +{ + RPiCameraData *data = cameraData(camera); + + if (!data->isRunning()) + return -EINVAL; + + LOG(RPI, Debug) << "queueRequestDevice: New request."; + + /* Push all buffers supplied in the Request to the respective streams. */ + for (auto stream : data->streams_) { + if (!stream->isExternal()) + continue; + + FrameBuffer *buffer = request->findBuffer(stream); + if (buffer && stream->getBufferId(buffer) == -1) { + /* + * This buffer is not recognised, so it must have been allocated + * outside the v4l2 device. Store it in the stream buffer list + * so we can track it. + */ + stream->setExternalBuffer(buffer); + } + + /* + * If no buffer is provided by the request for this stream, we + * queue a nullptr to the stream to signify that it must use an + * internally allocated buffer for this capture request. This + * buffer will not be given back to the application, but is used + * to support the internal pipeline flow. + * + * The below queueBuffer() call will do nothing if there are not + * enough internal buffers allocated, but this will be handled by + * queuing the request for buffers in the RPiStream object. + */ + int ret = stream->queueBuffer(buffer); + if (ret) + return ret; + } + + /* Push the request to the back of the queue. */ + data->requestQueue_.push_back(request); + data->handleState(); + + return 0; +} + +bool PipelineHandlerRPi::match(DeviceEnumerator *enumerator) +{ + constexpr unsigned int numUnicamDevices = 2; + + /* + * Loop over all Unicam instances, but return out once a match is found. + * This is to ensure we correctly enumrate the camera when an instance + * of Unicam has registered with media controller, but has not registered + * device nodes due to a sensor subdevice failure. + */ + for (unsigned int i = 0; i < numUnicamDevices; i++) { + DeviceMatch unicam("unicam"); + MediaDevice *unicamDevice = acquireMediaDevice(enumerator, unicam); + + if (!unicamDevice) { + LOG(RPI, Debug) << "Unable to acquire a Unicam instance"; + continue; + } + + DeviceMatch isp("bcm2835-isp"); + MediaDevice *ispDevice = acquireMediaDevice(enumerator, isp); + + if (!ispDevice) { + LOG(RPI, Debug) << "Unable to acquire ISP instance"; + continue; + } + + /* + * The loop below is used to register multiple cameras behind one or more + * video mux devices that are attached to a particular Unicam instance. + * Obviously these cameras cannot be used simultaneously. + */ + unsigned int numCameras = 0; + for (MediaEntity *entity : unicamDevice->entities()) { + if (entity->function() != MEDIA_ENT_F_CAM_SENSOR) + continue; + + int ret = registerCamera(unicamDevice, ispDevice, entity); + if (ret) + LOG(RPI, Error) << "Failed to register camera " + << entity->name() << ": " << ret; + else + numCameras++; + } + + if (numCameras) + return true; + } + + return false; +} + +void PipelineHandlerRPi::releaseDevice(Camera *camera) +{ + RPiCameraData *data = cameraData(camera); + data->freeBuffers(); +} + +int PipelineHandlerRPi::registerCamera(MediaDevice *unicam, MediaDevice *isp, MediaEntity *sensorEntity) +{ + std::unique_ptr data = std::make_unique(this); + + if (!data->dmaHeap_.isValid()) + return -ENOMEM; + + MediaEntity *unicamImage = unicam->getEntityByName("unicam-image"); + MediaEntity *ispOutput0 = isp->getEntityByName("bcm2835-isp0-output0"); + MediaEntity *ispCapture1 = isp->getEntityByName("bcm2835-isp0-capture1"); + MediaEntity *ispCapture2 = isp->getEntityByName("bcm2835-isp0-capture2"); + MediaEntity *ispCapture3 = isp->getEntityByName("bcm2835-isp0-capture3"); + + if (!unicamImage || !ispOutput0 || !ispCapture1 || !ispCapture2 || !ispCapture3) + return -ENOENT; + + /* Locate and open the unicam video streams. */ + data->unicam_[Unicam::Image] = RPi::Stream("Unicam Image", unicamImage); + + /* An embedded data node will not be present if the sensor does not support it. */ + MediaEntity *unicamEmbedded = unicam->getEntityByName("unicam-embedded"); + if (unicamEmbedded) { + data->unicam_[Unicam::Embedded] = RPi::Stream("Unicam Embedded", unicamEmbedded); + data->unicam_[Unicam::Embedded].dev()->bufferReady.connect(data.get(), + &RPiCameraData::unicamBufferDequeue); + } + + /* Tag the ISP input stream as an import stream. */ + data->isp_[Isp::Input] = RPi::Stream("ISP Input", ispOutput0, true); + data->isp_[Isp::Output0] = RPi::Stream("ISP Output0", ispCapture1); + data->isp_[Isp::Output1] = RPi::Stream("ISP Output1", ispCapture2); + data->isp_[Isp::Stats] = RPi::Stream("ISP Stats", ispCapture3); + + /* Wire up all the buffer connections. */ + data->unicam_[Unicam::Image].dev()->dequeueTimeout.connect(data.get(), &RPiCameraData::unicamTimeout); + data->unicam_[Unicam::Image].dev()->frameStart.connect(data.get(), &RPiCameraData::frameStarted); + data->unicam_[Unicam::Image].dev()->bufferReady.connect(data.get(), &RPiCameraData::unicamBufferDequeue); + data->isp_[Isp::Input].dev()->bufferReady.connect(data.get(), &RPiCameraData::ispInputDequeue); + data->isp_[Isp::Output0].dev()->bufferReady.connect(data.get(), &RPiCameraData::ispOutputDequeue); + data->isp_[Isp::Output1].dev()->bufferReady.connect(data.get(), &RPiCameraData::ispOutputDequeue); + data->isp_[Isp::Stats].dev()->bufferReady.connect(data.get(), &RPiCameraData::ispOutputDequeue); + + data->sensor_ = std::make_unique(sensorEntity); + if (!data->sensor_) + return -EINVAL; + + if (data->sensor_->init()) + return -EINVAL; + + /* + * Enumerate all the Video Mux/Bridge devices across the sensor -> unicam + * chain. There may be a cascade of devices in this chain! + */ + MediaLink *link = sensorEntity->getPadByIndex(0)->links()[0]; + data->enumerateVideoDevices(link); + + data->sensorFormats_ = populateSensorFormats(data->sensor_); + + ipa::RPi::IPAInitResult result; + if (data->loadIPA(&result)) { + LOG(RPI, Error) << "Failed to load a suitable IPA library"; + return -EINVAL; + } + + if (result.sensorConfig.sensorMetadata ^ !!unicamEmbedded) { + LOG(RPI, Warning) << "Mismatch between Unicam and CamHelper for embedded data usage!"; + result.sensorConfig.sensorMetadata = false; + if (unicamEmbedded) + data->unicam_[Unicam::Embedded].dev()->bufferReady.disconnect(); + } + + /* + * Open all Unicam and ISP streams. The exception is the embedded data + * stream, which only gets opened below if the IPA reports that the sensor + * supports embedded data. + * + * The below grouping is just for convenience so that we can easily + * iterate over all streams in one go. + */ + data->streams_.push_back(&data->unicam_[Unicam::Image]); + if (result.sensorConfig.sensorMetadata) + data->streams_.push_back(&data->unicam_[Unicam::Embedded]); + + for (auto &stream : data->isp_) + data->streams_.push_back(&stream); + + for (auto stream : data->streams_) { + int ret = stream->dev()->open(); + if (ret) + return ret; + } + + if (!data->unicam_[Unicam::Image].dev()->caps().hasMediaController()) { + LOG(RPI, Error) << "Unicam driver does not use the MediaController, please update your kernel!"; + return -EINVAL; + } + + /* + * Setup our delayed control writer with the sensor default + * gain and exposure delays. Mark VBLANK for priority write. + */ + std::unordered_map params = { + { V4L2_CID_ANALOGUE_GAIN, { result.sensorConfig.gainDelay, false } }, + { V4L2_CID_EXPOSURE, { result.sensorConfig.exposureDelay, false } }, + { V4L2_CID_HBLANK, { result.sensorConfig.hblankDelay, false } }, + { V4L2_CID_VBLANK, { result.sensorConfig.vblankDelay, true } } + }; + data->delayedCtrls_ = std::make_unique(data->sensor_->device(), params); + data->sensorMetadata_ = result.sensorConfig.sensorMetadata; + + /* Register initial controls that the Raspberry Pi IPA can handle. */ + data->controlInfo_ = std::move(result.controlInfo); + + /* Initialize the camera properties. */ + data->properties_ = data->sensor_->properties(); + + /* + * The V4L2_CID_NOTIFY_GAINS control, if present, is used to inform the + * sensor of the colour gains. It is defined to be a linear gain where + * the default value represents a gain of exactly one. + */ + auto it = data->sensor_->controls().find(V4L2_CID_NOTIFY_GAINS); + if (it != data->sensor_->controls().end()) + data->notifyGainsUnity_ = it->second.def().get(); + + /* + * Set a default value for the ScalerCropMaximum property to show + * that we support its use, however, initialise it to zero because + * it's not meaningful until a camera mode has been chosen. + */ + data->properties_.set(properties::ScalerCropMaximum, Rectangle{}); + + /* + * We cache two things about the sensor in relation to transforms + * (meaning horizontal and vertical flips): if they affect the Bayer + * ordering, and what the "native" Bayer order is, when no transforms + * are applied. + * + * We note that the sensor's cached list of supported formats is + * already in the "native" order, with any flips having been undone. + */ + const V4L2Subdevice *sensor = data->sensor_->device(); + const struct v4l2_query_ext_ctrl *hflipCtrl = sensor->controlInfo(V4L2_CID_HFLIP); + if (hflipCtrl) { + /* We assume it will support vflips too... */ + data->flipsAlterBayerOrder_ = hflipCtrl->flags & V4L2_CTRL_FLAG_MODIFY_LAYOUT; + } + + /* Look for a valid Bayer format. */ + BayerFormat bayerFormat; + for (const auto &iter : data->sensorFormats_) { + bayerFormat = BayerFormat::fromMbusCode(iter.first); + if (bayerFormat.isValid()) + break; + } + + if (!bayerFormat.isValid()) { + LOG(RPI, Error) << "No Bayer format found"; + return -EINVAL; + } + data->nativeBayerOrder_ = bayerFormat.order; + + /* + * List the available streams an application may request. At present, we + * do not advertise Unicam Embedded and ISP Statistics streams, as there + * is no mechanism for the application to request non-image buffer formats. + */ + std::set streams; + streams.insert(&data->unicam_[Unicam::Image]); + streams.insert(&data->isp_[Isp::Output0]); + streams.insert(&data->isp_[Isp::Output1]); + + int ret = data->loadPipelineConfiguration(); + if (ret) { + LOG(RPI, Error) << "Unable to load pipeline configuration"; + return ret; + } + + /* Create and register the camera. */ + const std::string &id = data->sensor_->id(); + std::shared_ptr camera = + Camera::create(std::move(data), id, streams); + PipelineHandler::registerCamera(std::move(camera)); + + LOG(RPI, Info) << "Registered camera " << id + << " to Unicam device " << unicam->deviceNode() + << " and ISP device " << isp->deviceNode(); + return 0; +} + +int PipelineHandlerRPi::queueAllBuffers(Camera *camera) +{ + RPiCameraData *data = cameraData(camera); + int ret; + + for (auto const stream : data->streams_) { + if (!stream->isExternal()) { + ret = stream->queueAllBuffers(); + if (ret < 0) + return ret; + } else { + /* + * For external streams, we must queue up a set of internal + * buffers to handle the number of drop frames requested by + * the IPA. This is done by passing nullptr in queueBuffer(). + * + * The below queueBuffer() call will do nothing if there + * are not enough internal buffers allocated, but this will + * be handled by queuing the request for buffers in the + * RPiStream object. + */ + unsigned int i; + for (i = 0; i < data->dropFrameCount_; i++) { + ret = stream->queueBuffer(nullptr); + if (ret) + return ret; + } + } + } + + return 0; +} + +int PipelineHandlerRPi::prepareBuffers(Camera *camera) +{ + RPiCameraData *data = cameraData(camera); + unsigned int numRawBuffers = 0; + int ret; + + for (Stream *s : camera->streams()) { + if (isRaw(s->configuration().pixelFormat)) { + numRawBuffers = s->configuration().bufferCount; + break; + } + } + + /* Decide how many internal buffers to allocate. */ + for (auto const stream : data->streams_) { + unsigned int numBuffers; + /* + * For Unicam, allocate a minimum number of buffers for internal + * use as we want to avoid any frame drops. + */ + const unsigned int minBuffers = data->config_.minTotalUnicamBuffers; + if (stream == &data->unicam_[Unicam::Image]) { + /* + * If an application has configured a RAW stream, allocate + * additional buffers to make up the minimum, but ensure + * we have at least minUnicamBuffers of internal buffers + * to use to minimise frame drops. + */ + numBuffers = std::max(data->config_.minUnicamBuffers, + minBuffers - numRawBuffers); + } else if (stream == &data->isp_[Isp::Input]) { + /* + * ISP input buffers are imported from Unicam, so follow + * similar logic as above to count all the RAW buffers + * available. + */ + numBuffers = numRawBuffers + + std::max(data->config_.minUnicamBuffers, + minBuffers - numRawBuffers); + + } else if (stream == &data->unicam_[Unicam::Embedded]) { + /* + * Embedded data buffers are (currently) for internal use, + * so allocate the minimum required to avoid frame drops. + */ + numBuffers = minBuffers; + } else { + /* + * Since the ISP runs synchronous with the IPA and requests, + * we only ever need one set of internal buffers. Any buffers + * the application wants to hold onto will already be exported + * through PipelineHandlerRPi::exportFrameBuffers(). + */ + numBuffers = 1; + } + + ret = stream->prepareBuffers(numBuffers); + if (ret < 0) + return ret; + } + + /* + * Pass the stats and embedded data buffers to the IPA. No other + * buffers need to be passed. + */ + mapBuffers(camera, data->isp_[Isp::Stats].getBuffers(), RPi::MaskStats); + if (data->sensorMetadata_) + mapBuffers(camera, data->unicam_[Unicam::Embedded].getBuffers(), + RPi::MaskEmbeddedData); + + return 0; +} + +void PipelineHandlerRPi::mapBuffers(Camera *camera, const RPi::BufferMap &buffers, unsigned int mask) +{ + RPiCameraData *data = cameraData(camera); + std::vector ipaBuffers; + /* + * Link the FrameBuffers with the id (key value) in the map stored in + * the RPi stream object - along with an identifier mask. + * + * This will allow us to identify buffers passed between the pipeline + * handler and the IPA. + */ + for (auto const &it : buffers) { + ipaBuffers.push_back(IPABuffer(mask | it.first, + it.second->planes())); + data->ipaBuffers_.insert(mask | it.first); + } + + data->ipa_->mapBuffers(ipaBuffers); +} + +void RPiCameraData::freeBuffers() +{ + if (ipa_) { + /* + * Copy the buffer ids from the unordered_set to a vector to + * pass to the IPA. + */ + std::vector ipaBuffers(ipaBuffers_.begin(), + ipaBuffers_.end()); + ipa_->unmapBuffers(ipaBuffers); + ipaBuffers_.clear(); + } + + for (auto const stream : streams_) + stream->releaseBuffers(); + + buffersAllocated_ = false; +} + +void RPiCameraData::frameStarted(uint32_t sequence) +{ + LOG(RPI, Debug) << "frame start " << sequence; + + /* Write any controls for the next frame as soon as we can. */ + delayedCtrls_->applyControls(sequence); +} + +int RPiCameraData::loadIPA(ipa::RPi::IPAInitResult *result) +{ + ipa_ = IPAManager::createIPA(pipe(), 1, 1); + + if (!ipa_) + return -ENOENT; + + ipa_->statsMetadataComplete.connect(this, &RPiCameraData::statsMetadataComplete); + ipa_->runIsp.connect(this, &RPiCameraData::runIsp); + ipa_->embeddedComplete.connect(this, &RPiCameraData::embeddedComplete); + ipa_->setIspControls.connect(this, &RPiCameraData::setIspControls); + ipa_->setDelayedControls.connect(this, &RPiCameraData::setDelayedControls); + ipa_->setLensControls.connect(this, &RPiCameraData::setLensControls); + ipa_->setCameraTimeout.connect(this, &RPiCameraData::setCameraTimeout); + + /* + * The configuration (tuning file) is made from the sensor name unless + * the environment variable overrides it. + */ + std::string configurationFile; + char const *configFromEnv = utils::secure_getenv("LIBCAMERA_RPI_TUNING_FILE"); + if (!configFromEnv || *configFromEnv == '\0') { + std::string model = sensor_->model(); + if (isMonoSensor(sensor_)) + model += "_mono"; + configurationFile = ipa_->configurationFile(model + ".json"); + } else { + configurationFile = std::string(configFromEnv); + } + + IPASettings settings(configurationFile, sensor_->model()); + + return ipa_->init(settings, !!sensor_->focusLens(), result); +} + +int RPiCameraData::configureIPA(const CameraConfiguration *config, ipa::RPi::IPAConfigResult *result) +{ + std::map entityControls; + ipa::RPi::IPAConfig ipaConfig; + + /* \todo Move passing of ispControls and lensControls to ipa::init() */ + ipaConfig.sensorControls = sensor_->controls(); + ipaConfig.ispControls = isp_[Isp::Input].dev()->controls(); + if (sensor_->focusLens()) + ipaConfig.lensControls = sensor_->focusLens()->controls(); + + /* Always send the user transform to the IPA. */ + ipaConfig.transform = static_cast(config->transform); + + /* Allocate the lens shading table via dmaHeap and pass to the IPA. */ + if (!lsTable_.isValid()) { + lsTable_ = SharedFD(dmaHeap_.alloc("ls_grid", ipa::RPi::MaxLsGridSize)); + if (!lsTable_.isValid()) + return -ENOMEM; + + /* Allow the IPA to mmap the LS table via the file descriptor. */ + /* + * \todo Investigate if mapping the lens shading table buffer + * could be handled with mapBuffers(). + */ + ipaConfig.lsTableHandle = lsTable_; + } + + /* We store the IPACameraSensorInfo for digital zoom calculations. */ + int ret = sensor_->sensorInfo(&sensorInfo_); + if (ret) { + LOG(RPI, Error) << "Failed to retrieve camera sensor info"; + return ret; + } + + /* Ready the IPA - it must know about the sensor resolution. */ + ControlList controls; + ret = ipa_->configure(sensorInfo_, ipaConfig, &controls, result); + if (ret < 0) { + LOG(RPI, Error) << "IPA configuration failed!"; + return -EPIPE; + } + + if (!controls.empty()) + setSensorControls(controls); + + return 0; +} + +int RPiCameraData::loadPipelineConfiguration() +{ + config_ = { + .minUnicamBuffers = 2, + .minTotalUnicamBuffers = 4, + .disableStartupFrameDrops = false, + .unicamTimeoutValue = 0, + }; + + char const *configFromEnv = utils::secure_getenv("LIBCAMERA_RPI_CONFIG_FILE"); + if (!configFromEnv || *configFromEnv == '\0') + return 0; + + std::string filename = std::string(configFromEnv); + File file(filename); + + if (!file.open(File::OpenModeFlag::ReadOnly)) { + LOG(RPI, Error) << "Failed to open configuration file '" << filename << "'"; + return -EIO; + } + + LOG(RPI, Info) << "Using configuration file '" << filename << "'"; + + std::unique_ptr root = YamlParser::parse(file); + if (!root) { + LOG(RPI, Warning) << "Failed to parse configuration file, using defaults"; + return 0; + } + + std::optional ver = (*root)["version"].get(); + if (!ver || *ver != 1.0) { + LOG(RPI, Error) << "Unexpected configuration file version reported"; + return -EINVAL; + } + + const YamlObject &phConfig = (*root)["pipeline_handler"]; + config_.minUnicamBuffers = + phConfig["min_unicam_buffers"].get(config_.minUnicamBuffers); + config_.minTotalUnicamBuffers = + phConfig["min_total_unicam_buffers"].get(config_.minTotalUnicamBuffers); + config_.disableStartupFrameDrops = + phConfig["disable_startup_frame_drops"].get(config_.disableStartupFrameDrops); + config_.unicamTimeoutValue = + phConfig["unicam_timeout_value_ms"].get(config_.unicamTimeoutValue); + + if (config_.unicamTimeoutValue) { + /* Disable the IPA signal to control timeout and set the user requested value. */ + ipa_->setCameraTimeout.disconnect(); + unicam_[Unicam::Image].dev()->setDequeueTimeout(config_.unicamTimeoutValue * 1ms); + } + + if (config_.minTotalUnicamBuffers < config_.minUnicamBuffers) { + LOG(RPI, Error) << "Invalid configuration: min_total_unicam_buffers must be >= min_unicam_buffers"; + return -EINVAL; + } + + if (config_.minTotalUnicamBuffers < 1) { + LOG(RPI, Error) << "Invalid configuration: min_total_unicam_buffers must be >= 1"; + return -EINVAL; + } + + return 0; +} + +/* + * enumerateVideoDevices() iterates over the Media Controller topology, starting + * at the sensor and finishing at Unicam. For each sensor, RPiCameraData stores + * a unique list of any intermediate video mux or bridge devices connected in a + * cascade, together with the entity to entity link. + * + * Entity pad configuration and link enabling happens at the end of configure(). + * We first disable all pad links on each entity device in the chain, and then + * selectively enabling the specific links to link sensor to Unicam across all + * intermediate muxes and bridges. + * + * In the cascaded topology below, if Sensor1 is used, the Mux2 -> Mux1 link + * will be disabled, and Sensor1 -> Mux1 -> Unicam links enabled. Alternatively, + * if Sensor3 is used, the Sensor2 -> Mux2 and Sensor1 -> Mux1 links are disabled, + * and Sensor3 -> Mux2 -> Mux1 -> Unicam links are enabled. All other links will + * remain unchanged. + * + * +----------+ + * | Unicam | + * +-----^----+ + * | + * +---+---+ + * | Mux1 <-------+ + * +--^----+ | + * | | + * +-----+---+ +---+---+ + * | Sensor1 | | Mux2 |<--+ + * +---------+ +-^-----+ | + * | | + * +-------+-+ +---+-----+ + * | Sensor2 | | Sensor3 | + * +---------+ +---------+ + */ +void RPiCameraData::enumerateVideoDevices(MediaLink *link) +{ + const MediaPad *sinkPad = link->sink(); + const MediaEntity *entity = sinkPad->entity(); + bool unicamFound = false; + + /* We only deal with Video Mux and Bridge devices in cascade. */ + if (entity->function() != MEDIA_ENT_F_VID_MUX && + entity->function() != MEDIA_ENT_F_VID_IF_BRIDGE) + return; + + /* Find the source pad for this Video Mux or Bridge device. */ + const MediaPad *sourcePad = nullptr; + for (const MediaPad *pad : entity->pads()) { + if (pad->flags() & MEDIA_PAD_FL_SOURCE) { + /* + * We can only deal with devices that have a single source + * pad. If this device has multiple source pads, ignore it + * and this branch in the cascade. + */ + if (sourcePad) + return; + + sourcePad = pad; + } + } + + LOG(RPI, Debug) << "Found video mux device " << entity->name() + << " linked to sink pad " << sinkPad->index(); + + bridgeDevices_.emplace_back(std::make_unique(entity), link); + bridgeDevices_.back().first->open(); + + /* + * Iterate through all the sink pad links down the cascade to find any + * other Video Mux and Bridge devices. + */ + for (MediaLink *l : sourcePad->links()) { + enumerateVideoDevices(l); + /* Once we reach the Unicam entity, we are done. */ + if (l->sink()->entity()->name() == "unicam-image") { + unicamFound = true; + break; + } + } + + /* This identifies the end of our entity enumeration recursion. */ + if (link->source()->entity()->function() == MEDIA_ENT_F_CAM_SENSOR) { + /* + * If Unicam is not at the end of this cascade, we cannot configure + * this topology automatically, so remove all entity references. + */ + if (!unicamFound) { + LOG(RPI, Warning) << "Cannot automatically configure this MC topology!"; + bridgeDevices_.clear(); + } + } +} + +void RPiCameraData::statsMetadataComplete(uint32_t bufferId, const ControlList &controls) +{ + if (!isRunning()) + return; + + FrameBuffer *buffer = isp_[Isp::Stats].getBuffers().at(bufferId & RPi::MaskID); + + handleStreamBuffer(buffer, &isp_[Isp::Stats]); + + /* Add to the Request metadata buffer what the IPA has provided. */ + Request *request = requestQueue_.front(); + request->metadata().merge(controls); + + /* + * Inform the sensor of the latest colour gains if it has the + * V4L2_CID_NOTIFY_GAINS control (which means notifyGainsUnity_ is set). + */ + const auto &colourGains = controls.get(libcamera::controls::ColourGains); + if (notifyGainsUnity_ && colourGains) { + /* The control wants linear gains in the order B, Gb, Gr, R. */ + ControlList ctrls(sensor_->controls()); + std::array gains{ + static_cast((*colourGains)[1] * *notifyGainsUnity_), + *notifyGainsUnity_, + *notifyGainsUnity_, + static_cast((*colourGains)[0] * *notifyGainsUnity_) + }; + ctrls.set(V4L2_CID_NOTIFY_GAINS, Span{ gains }); + + sensor_->setControls(&ctrls); + } + + state_ = State::IpaComplete; + handleState(); +} + +void RPiCameraData::runIsp(uint32_t bufferId) +{ + if (!isRunning()) + return; + + FrameBuffer *buffer = unicam_[Unicam::Image].getBuffers().at(bufferId & RPi::MaskID); + + LOG(RPI, Debug) << "Input re-queue to ISP, buffer id " << (bufferId & RPi::MaskID) + << ", timestamp: " << buffer->metadata().timestamp; + + isp_[Isp::Input].queueBuffer(buffer); + ispOutputCount_ = 0; + handleState(); +} + +void RPiCameraData::embeddedComplete(uint32_t bufferId) +{ + if (!isRunning()) + return; + + FrameBuffer *buffer = unicam_[Unicam::Embedded].getBuffers().at(bufferId & RPi::MaskID); + handleStreamBuffer(buffer, &unicam_[Unicam::Embedded]); + handleState(); +} + +void RPiCameraData::setIspControls(const ControlList &controls) +{ + ControlList ctrls = controls; + + if (ctrls.contains(V4L2_CID_USER_BCM2835_ISP_LENS_SHADING)) { + ControlValue &value = + const_cast(ctrls.get(V4L2_CID_USER_BCM2835_ISP_LENS_SHADING)); + Span s = value.data(); + bcm2835_isp_lens_shading *ls = + reinterpret_cast(s.data()); + ls->dmabuf = lsTable_.get(); + } + + isp_[Isp::Input].dev()->setControls(&ctrls); + handleState(); +} + +void RPiCameraData::setDelayedControls(const ControlList &controls, uint32_t delayContext) +{ + if (!delayedCtrls_->push(controls, delayContext)) + LOG(RPI, Error) << "V4L2 DelayedControl set failed"; + handleState(); +} + +void RPiCameraData::setLensControls(const ControlList &controls) +{ + CameraLens *lens = sensor_->focusLens(); + + if (lens && controls.contains(V4L2_CID_FOCUS_ABSOLUTE)) { + ControlValue const &focusValue = controls.get(V4L2_CID_FOCUS_ABSOLUTE); + lens->setFocusPosition(focusValue.get()); + } +} + +void RPiCameraData::setCameraTimeout(uint32_t maxFrameLengthMs) +{ + /* + * Set the dequeue timeout to the larger of 5x the maximum reported + * frame length advertised by the IPA over a number of frames. Allow + * a minimum timeout value of 1s. + */ + utils::Duration timeout = + std::max(1s, 5 * maxFrameLengthMs * 1ms); + + LOG(RPI, Debug) << "Setting Unicam timeout to " << timeout; + unicam_[Unicam::Image].dev()->setDequeueTimeout(timeout); +} + +void RPiCameraData::setSensorControls(ControlList &controls) +{ + /* + * We need to ensure that if both VBLANK and EXPOSURE are present, the + * former must be written ahead of, and separately from EXPOSURE to avoid + * V4L2 rejecting the latter. This is identical to what DelayedControls + * does with the priority write flag. + * + * As a consequence of the below logic, VBLANK gets set twice, and we + * rely on the v4l2 framework to not pass the second control set to the + * driver as the actual control value has not changed. + */ + if (controls.contains(V4L2_CID_EXPOSURE) && controls.contains(V4L2_CID_VBLANK)) { + ControlList vblank_ctrl; + + vblank_ctrl.set(V4L2_CID_VBLANK, controls.get(V4L2_CID_VBLANK)); + sensor_->setControls(&vblank_ctrl); + } + + sensor_->setControls(&controls); +} + +void RPiCameraData::unicamTimeout() +{ + LOG(RPI, Error) << "Unicam has timed out!"; + LOG(RPI, Error) << "Please check that your camera sensor connector is attached securely."; + LOG(RPI, Error) << "Alternatively, try another cable and/or sensor."; + + state_ = RPiCameraData::State::Error; + /* + * To allow the application to attempt a recovery from this timeout, + * stop all devices streaming, and return any outstanding requests as + * incomplete and cancelled. + */ + for (auto const stream : streams_) + stream->dev()->streamOff(); + + clearIncompleteRequests(); +} + +void RPiCameraData::unicamBufferDequeue(FrameBuffer *buffer) +{ + RPi::Stream *stream = nullptr; + int index; + + if (!isRunning()) + return; + + for (RPi::Stream &s : unicam_) { + index = s.getBufferId(buffer); + if (index != -1) { + stream = &s; + break; + } + } + + /* The buffer must belong to one of our streams. */ + ASSERT(stream); + + LOG(RPI, Debug) << "Stream " << stream->name() << " buffer dequeue" + << ", buffer id " << index + << ", timestamp: " << buffer->metadata().timestamp; + + if (stream == &unicam_[Unicam::Image]) { + /* + * Lookup the sensor controls used for this frame sequence from + * DelayedControl and queue them along with the frame buffer. + */ + auto [ctrl, delayContext] = delayedCtrls_->get(buffer->metadata().sequence); + /* + * Add the frame timestamp to the ControlList for the IPA to use + * as it does not receive the FrameBuffer object. + */ + ctrl.set(controls::SensorTimestamp, buffer->metadata().timestamp); + bayerQueue_.push({ buffer, std::move(ctrl), delayContext }); + } else { + embeddedQueue_.push(buffer); + } + + handleState(); +} + +void RPiCameraData::ispInputDequeue(FrameBuffer *buffer) +{ + if (!isRunning()) + return; + + LOG(RPI, Debug) << "Stream ISP Input buffer complete" + << ", buffer id " << unicam_[Unicam::Image].getBufferId(buffer) + << ", timestamp: " << buffer->metadata().timestamp; + + /* The ISP input buffer gets re-queued into Unicam. */ + handleStreamBuffer(buffer, &unicam_[Unicam::Image]); + handleState(); +} + +void RPiCameraData::ispOutputDequeue(FrameBuffer *buffer) +{ + RPi::Stream *stream = nullptr; + int index; + + if (!isRunning()) + return; + + for (RPi::Stream &s : isp_) { + index = s.getBufferId(buffer); + if (index != -1) { + stream = &s; + break; + } + } + + /* The buffer must belong to one of our ISP output streams. */ + ASSERT(stream); + + LOG(RPI, Debug) << "Stream " << stream->name() << " buffer complete" + << ", buffer id " << index + << ", timestamp: " << buffer->metadata().timestamp; + + /* + * ISP statistics buffer must not be re-queued or sent back to the + * application until after the IPA signals so. + */ + if (stream == &isp_[Isp::Stats]) { + ipa_->signalStatReady(RPi::MaskStats | static_cast(index), + requestQueue_.front()->sequence()); + } else { + /* Any other ISP output can be handed back to the application now. */ + handleStreamBuffer(buffer, stream); + } + + /* + * Increment the number of ISP outputs generated. + * This is needed to track dropped frames. + */ + ispOutputCount_++; + + handleState(); +} + +void RPiCameraData::clearIncompleteRequests() +{ + /* + * All outstanding requests (and associated buffers) must be returned + * back to the application. + */ + while (!requestQueue_.empty()) { + Request *request = requestQueue_.front(); + + for (auto &b : request->buffers()) { + FrameBuffer *buffer = b.second; + /* + * Has the buffer already been handed back to the + * request? If not, do so now. + */ + if (buffer->request()) { + buffer->_d()->cancel(); + pipe()->completeBuffer(request, buffer); + } + } + + pipe()->completeRequest(request); + requestQueue_.pop_front(); + } +} + +void RPiCameraData::handleStreamBuffer(FrameBuffer *buffer, RPi::Stream *stream) +{ + /* + * It is possible to be here without a pending request, so check + * that we actually have one to action, otherwise we just return + * buffer back to the stream. + */ + Request *request = requestQueue_.empty() ? nullptr : requestQueue_.front(); + if (!dropFrameCount_ && request && request->findBuffer(stream) == buffer) { + /* + * Check if this is an externally provided buffer, and if + * so, we must stop tracking it in the pipeline handler. + */ + handleExternalBuffer(buffer, stream); + /* + * Tag the buffer as completed, returning it to the + * application. + */ + pipe()->completeBuffer(request, buffer); + } else { + /* + * This buffer was not part of the Request (which happens if an + * internal buffer was used for an external stream, or + * unconditionally for internal streams), or there is no pending + * request, so we can recycle it. + */ + stream->returnBuffer(buffer); + } +} + +void RPiCameraData::handleExternalBuffer(FrameBuffer *buffer, RPi::Stream *stream) +{ + unsigned int id = stream->getBufferId(buffer); + + if (!(id & RPi::MaskExternalBuffer)) + return; + + /* Stop the Stream object from tracking the buffer. */ + stream->removeExternalBuffer(buffer); +} + +void RPiCameraData::handleState() +{ + switch (state_) { + case State::Stopped: + case State::Busy: + case State::Error: + break; + + case State::IpaComplete: + /* If the request is completed, we will switch to Idle state. */ + checkRequestCompleted(); + /* + * No break here, we want to try running the pipeline again. + * The fallthrough clause below suppresses compiler warnings. + */ + [[fallthrough]]; + + case State::Idle: + tryRunPipeline(); + break; + } +} + +void RPiCameraData::checkRequestCompleted() +{ + bool requestCompleted = false; + /* + * If we are dropping this frame, do not touch the request, simply + * change the state to IDLE when ready. + */ + if (!dropFrameCount_) { + Request *request = requestQueue_.front(); + if (request->hasPendingBuffers()) + return; + + /* Must wait for metadata to be filled in before completing. */ + if (state_ != State::IpaComplete) + return; + + pipe()->completeRequest(request); + requestQueue_.pop_front(); + requestCompleted = true; + } + + /* + * Make sure we have three outputs completed in the case of a dropped + * frame. + */ + if (state_ == State::IpaComplete && + ((ispOutputCount_ == 3 && dropFrameCount_) || requestCompleted)) { + state_ = State::Idle; + if (dropFrameCount_) { + dropFrameCount_--; + LOG(RPI, Debug) << "Dropping frame at the request of the IPA (" + << dropFrameCount_ << " left)"; + } + } +} + +Rectangle RPiCameraData::scaleIspCrop(const Rectangle &ispCrop) const +{ + /* + * Scale a crop rectangle defined in the ISP's coordinates into native sensor + * coordinates. + */ + Rectangle nativeCrop = ispCrop.scaledBy(sensorInfo_.analogCrop.size(), + sensorInfo_.outputSize); + nativeCrop.translateBy(sensorInfo_.analogCrop.topLeft()); + return nativeCrop; +} + +void RPiCameraData::applyScalerCrop(const ControlList &controls) +{ + const auto &scalerCrop = controls.get(controls::ScalerCrop); + if (scalerCrop) { + Rectangle nativeCrop = *scalerCrop; + + if (!nativeCrop.width || !nativeCrop.height) + nativeCrop = { 0, 0, 1, 1 }; + + /* Create a version of the crop scaled to ISP (camera mode) pixels. */ + Rectangle ispCrop = nativeCrop.translatedBy(-sensorInfo_.analogCrop.topLeft()); + ispCrop.scaleBy(sensorInfo_.outputSize, sensorInfo_.analogCrop.size()); + + /* + * The crop that we set must be: + * 1. At least as big as ispMinCropSize_, once that's been + * enlarged to the same aspect ratio. + * 2. With the same mid-point, if possible. + * 3. But it can't go outside the sensor area. + */ + Size minSize = ispMinCropSize_.expandedToAspectRatio(nativeCrop.size()); + Size size = ispCrop.size().expandedTo(minSize); + ispCrop = size.centeredTo(ispCrop.center()).enclosedIn(Rectangle(sensorInfo_.outputSize)); + + if (ispCrop != ispCrop_) { + isp_[Isp::Input].dev()->setSelection(V4L2_SEL_TGT_CROP, &ispCrop); + ispCrop_ = ispCrop; + + /* + * Also update the ScalerCrop in the metadata with what we actually + * used. But we must first rescale that from ISP (camera mode) pixels + * back into sensor native pixels. + */ + scalerCrop_ = scaleIspCrop(ispCrop_); + } + } +} + +void RPiCameraData::fillRequestMetadata(const ControlList &bufferControls, + Request *request) +{ + request->metadata().set(controls::SensorTimestamp, + bufferControls.get(controls::SensorTimestamp).value_or(0)); + + request->metadata().set(controls::ScalerCrop, scalerCrop_); +} + +void RPiCameraData::tryRunPipeline() +{ + FrameBuffer *embeddedBuffer; + BayerFrame bayerFrame; + + /* If any of our request or buffer queues are empty, we cannot proceed. */ + if (state_ != State::Idle || requestQueue_.empty() || + bayerQueue_.empty() || (embeddedQueue_.empty() && sensorMetadata_)) + return; + + if (!findMatchingBuffers(bayerFrame, embeddedBuffer)) + return; + + /* Take the first request from the queue and action the IPA. */ + Request *request = requestQueue_.front(); + + /* See if a new ScalerCrop value needs to be applied. */ + applyScalerCrop(request->controls()); + + /* + * Clear the request metadata and fill it with some initial non-IPA + * related controls. We clear it first because the request metadata + * may have been populated if we have dropped the previous frame. + */ + request->metadata().clear(); + fillRequestMetadata(bayerFrame.controls, request); + + /* + * Process all the user controls by the IPA. Once this is complete, we + * queue the ISP output buffer listed in the request to start the HW + * pipeline. + */ + ipa_->signalQueueRequest(request->controls()); + + /* Set our state to say the pipeline is active. */ + state_ = State::Busy; + + unsigned int bayerId = unicam_[Unicam::Image].getBufferId(bayerFrame.buffer); + + LOG(RPI, Debug) << "Signalling signalIspPrepare:" + << " Bayer buffer id: " << bayerId; + + ipa::RPi::ISPConfig ispPrepare; + ispPrepare.bayerBufferId = RPi::MaskBayerData | bayerId; + ispPrepare.controls = std::move(bayerFrame.controls); + ispPrepare.ipaContext = request->sequence(); + ispPrepare.delayContext = bayerFrame.delayContext; + + if (embeddedBuffer) { + unsigned int embeddedId = unicam_[Unicam::Embedded].getBufferId(embeddedBuffer); + + ispPrepare.embeddedBufferId = RPi::MaskEmbeddedData | embeddedId; + ispPrepare.embeddedBufferPresent = true; + + LOG(RPI, Debug) << "Signalling signalIspPrepare:" + << " Embedded buffer id: " << embeddedId; + } + + ipa_->signalIspPrepare(ispPrepare); +} + +bool RPiCameraData::findMatchingBuffers(BayerFrame &bayerFrame, FrameBuffer *&embeddedBuffer) +{ + if (bayerQueue_.empty()) + return false; + + /* + * Find the embedded data buffer with a matching timestamp to pass to + * the IPA. Any embedded buffers with a timestamp lower than the + * current bayer buffer will be removed and re-queued to the driver. + */ + uint64_t ts = bayerQueue_.front().buffer->metadata().timestamp; + embeddedBuffer = nullptr; + while (!embeddedQueue_.empty()) { + FrameBuffer *b = embeddedQueue_.front(); + if (b->metadata().timestamp < ts) { + embeddedQueue_.pop(); + unicam_[Unicam::Embedded].returnBuffer(b); + LOG(RPI, Debug) << "Dropping unmatched input frame in stream " + << unicam_[Unicam::Embedded].name(); + } else if (b->metadata().timestamp == ts) { + /* Found a match! */ + embeddedBuffer = b; + embeddedQueue_.pop(); + break; + } else { + break; /* Only higher timestamps from here. */ + } + } + + if (!embeddedBuffer && sensorMetadata_) { + if (embeddedQueue_.empty()) { + /* + * If the embedded buffer queue is empty, wait for the next + * buffer to arrive - dequeue ordering may send the image + * buffer first. + */ + LOG(RPI, Debug) << "Waiting for next embedded buffer."; + return false; + } + + /* Log if there is no matching embedded data buffer found. */ + LOG(RPI, Debug) << "Returning bayer frame without a matching embedded buffer."; + } + + bayerFrame = std::move(bayerQueue_.front()); + bayerQueue_.pop(); + + return true; +} + +REGISTER_PIPELINE_HANDLER(PipelineHandlerRPi) + +} /* namespace libcamera */ -- cgit v1.2.1